This week's book giveaway is in the OCPJP forum.
We're giving away four copies of OCA/OCP Java SE 7 Programmer I & II Study Guide and have Kathy Sierra & Bert Bates on-line!
See this thread for details.
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes Methods/variables opened a bit for testing purpose Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of OCA/OCP Java SE 7 Programmer I & II Study Guide this week in the OCPJP forum!
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "Methods/variables opened a bit for testing purpose" Watch "Methods/variables opened a bit for testing purpose" New topic
Author

Methods/variables opened a bit for testing purpose

Norbert Lebenthal
Ranch Hand

Joined: Sep 23, 2010
Posts: 74
hi

As I'm trying to do this SCJD properly, I unit test my work on the way.

Quickly I realized it would be handy to be able to access some variables and/or override some methods for my tests to be easier to do/run.

As these tests are in the same package as the tested class, I just need to switch some visibility from private to package private (or default visibility).

Do you think it's a major issue ?

I plan to comment it on per "visibility loosened" method/variable and then add a line for it in the text file to justify the whole stuff.

thanks in advance
best
nono
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5449
    
  13

I used JUnit too to test my Data class, but didn't change access modifiers just for testing purposes (only tests for the public contract of my classes I tested)


SCJA, SCJP (1.4 | 5.0 | 6.0), SCJD
http://www.javaroe.be/
Norbert Lebenthal
Ranch Hand

Joined: Sep 23, 2010
Posts: 74
hi Roel

thanks a lot for your answer. The link was especially good. In fact, it touches a topic where I still haven't found a proper answer for: how to do proper unit test (and reading book about it never really did it). Maybe it is the right time to address it: we have a proper example here, with this FileDatabaseAccess class which has to implement some given interface.

As such, how do you unit test your db file reader ? In my case, the constructor reads the file to figure out its structure and content (magic cookie, offset, record's fields name and length and so forth).

When coding, do you a write a test which needs this whole construction to be coded correctly first ? To test issue with part of this work, do you rely on some exception to be thrown in the constructor so that you can assert that what you expected to fail did actually ? And when multiple of your exceptions are and must be IOException ? Do you create your own IOException subclasses which will wrap the IOException found so you can be sure that the exception you caught is really from what you expected ?

To be more concrete, what about reading the fields name and length. For me, it's currently part of the constructor. Yet, some stuff happen before and some other after (reading the records for example). Yet I would like to test this functionality in isolation from the other, to be sure a bug somewhere else doesn't trigger a failing test for this piece of functionality and the other way around.

How would you do that ? Or is my goal not a good one ?

Sorry for this long post, but that's really a topic which I've many times pondered without proper answer. At work, we're ok with opening a bit some methods for testability purposes, and the tech lead is really good, so I thought it was a commonly accepted "OO degradation" for the sake of testability. Yet it always felt wrong, even if up to now I didn't have some better way.

thanks
nono
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5449
    
  13

If you would try to follow test-driven development, you should first write your test and then the actual code. I didn't do that. I wrote for example my read-method and then I wrote a number of test cases which gave me a 100% test coverage of that method. So that includes: a normal run, a wrong parameter, possible exceptions being thrown,... When all the tests succeeded, I started with the next method, and so on.

Because your Data class should be as generic as possible, you should hide implementation details. So my Data class does not throw any IOException, because when the database file is replaced with a RDBMS you would have to change all IOExceptions to SQLExceptions. Instead I created a more common DBException, which wraps the IOExceptions when needed.

The test case of a valid read:


An example of a test which has to throw an IllegalArgumentException:

At work some methods are also "opened" a bit for testability purposes, because of the complexness of these methods (for example: the business logic to validate if the social security number and the date of birth match each other is insane ) But I (we) try to limit this practice as much as possible. And this assignment does not have that complexity, or should not have it
Norbert Lebenthal
Ranch Hand

Joined: Sep 23, 2010
Posts: 74
Roel De Nijs wrote:If you would try to follow test-driven development, you should first write your test and then the actual code. I didn't do that.


Ok, I get better the story then

I won't get the perfect recipe for TDD then I fear, but it explains properly your non issue with testing the offset and the like in a TDD way.

thanks for the clarification

Roel De Nijs wrote:
At work some methods are also "opened" a bit for testability purposes, because of the complexness of these methods (for example: the business logic to validate if the social security number and the date of birth match each other is insane ) But I (we) try to limit this practice as much as possible. And this assignment does not have that complexity, or should not have it


well, I don't say an easy way (out of opening a bit the class) to do TDD on the Data access class creation personally. Yet I agree it's, in the end, bad practice. I thought of some kind of builder pattern, where the Data access class would be provided which an object having done (or being called to do) the parsing, but that would scatter the read/write logic in different classes: nothing I feel too appealing.

and thanks for the test cases, they're pretty closed from mine (except that I use JUnit 3, but it doesn't matter)

Roel De Nijs wrote:
Because your Data class should be as generic as possible, you should hide implementation details. So my Data class does not throw any IOException, because when the database file is replaced with a RDBMS you would have to change all IOExceptions to SQLExceptions. Instead I created a more common DBException, which wraps the IOExceptions when needed.


hum, you made me think with this line you know ? ;) lol

more seriously, I wonder if this is so true. Indeed, the locking mechanism applied here is very specific to the file approach. Any common database would handle on its own concurrent access, making the code written for the file database completely pointless (and even a performance/maintenance pain). So, IMHO, the day some real IT team would move to a proper database, they would be better dumping this code. Yet, in between, they're better of with the easier stuff to maintain. Which is my own personal goal there ;)

on the other hand, you raised enough my curiosity to ask a few more questions, if you don't mind: do you separate the writing of the String[] in some way ? I mean, putting it all in some string container/string builder is quite an uncommon DB access strategy. Similarly, how did you handle the offset/magic cookie stuff. It's once again really file data base specific... Don't tell me you did overridable method calls in your constructor ?;)

For the IOException, if I get it correctly, you decided not to thrown any but instead to throw a DBException which is a subclass of RuntimeException ? Good idea: the issues encountered could differ from a plain old IOError, and wrapping the original exception in a IOException feels really crappy.

thanks again for your availability in discussing this topic
Roel De Nijs
Bartender

Joined: Jul 19, 2004
Posts: 5449
    
  13

Norbert Lebenthal wrote:do you separate the writing of the String[] in some way ?

Just a simple (private) method which creates a byte[] from the String[] which is written to file. Nothing fancy, nothing complex.

Norbert Lebenthal wrote:Similarly, how did you handle the offset/magic cookie stuff.

Again very simple: magicCookie is read and compared against a hard-coded value. I don't have the offset in my file (I have record length instead). When I read the database file format I keep the offset stored in a private data member, so I can jump to the record section at any time.

Norbert Lebenthal wrote:you decided not to thrown any but instead to throw a DBException which is a subclass of RuntimeException ?

You have to implement the given interface, which has no "throws IOException" in method signatures, so you have to catch them. And you can't throw anything else but a runtime exception (otherwise compilation will fail). Because of my approach (using a record cache) I don't have any problems with the IOException (getting elements from a map does not throw any exception ). And I created my own interface (extending the given interface): with the new methods (e.g. initializing and destroying my cache) I could use checked exceptions. But the idea is the same: create a generic exception (DBException) which is used to wrap an IOException, so you are hiding the implementation details (so your code is protected from ripple effects caused by changes to the database implementation).
Norbert Lebenthal
Ranch Hand

Joined: Sep 23, 2010
Posts: 74
Roel De Nijs wrote:
Again very simple: magicCookie is read and compared against a hard-coded value. I don't have the offset in my file (I have record length instead). When I read the database file format I keep the offset stored in a private data member, so I can jump to the record section at any time.


thanks for the clarification. I've realized that, once again, I was about to overdo it ^^

Roel De Nijs wrote:
You have to implement the given interface, which has no "throws IOException" in method signatures, so you have to catch them. And you can't throw anything else but a runtime exception (otherwise compilation will fail).


yep, you're right. In fact I was doing from the DBAccess constructor, where throwing IOException is doable. But it wouldn't have worked anywhere else.

thanks again
Jamie Atkinson
Greenhorn

Joined: Sep 28, 2010
Posts: 1
Norbert Lebenthal wrote:
... I just need to switch some visibility from private to package private (or default visibility)....


Hi, you can use reflection within your test cases to test otherwise inaccessible methods. The benefit of this approach is that you do not need to alter any of your existing DAO implementation.

Here is an example where I am calling a private "readCookie" method on Data class which takes 1 int argument (no. of bytes to read).



Method.invoke will return an Object. Do an assertion on that.
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
 
subject: Methods/variables opened a bit for testing purpose