rc3.org

Strong opinions, weakly held

Package scope and unit testing with Java

I have a Java unit testing question. First, let me describe a scenario. Let’s say I have a class called IncomeTaxReturn that includes a method, calculateRefund(). That method is public (meaning anybody can call it). I also have a bunch of other methods that are only used by calculateRefund() that package up bits of logic so that the code is easier to understand and to test. The class might include methods like calculateTotalIncome() or lookUpTaxRate().

The methods I’ve written to encapsulate bits of business logic produce a predictable output given a specific input, and I want to write tests for them. I may also have some tests for calculateRefund(), but having tests for the other methods is a good practice because it helps to isolate problems when performing regression tests later.

Most Java projects keep tests in a directory structure parallel to the source for the application itself, making it easy to leave out the unit tests when building the application for distribution or deployment to the server.

I feel like the best practice is to mirror the package names between your application source and your tests. So if my IncomeTaxReturn class is in gov.irs.incometax then I put TestIncomeTaxReturn in the same package, gov.irs.incometax, only in the test directory. Then I just make sure that all of the classes for the application and tests are in the classpath when I want to run my tests.

So here’s the question. If I were not going to write any tests for the methods I was talking about, I’d make those methods private. If I want to test them, how do I handle the scope?

I can’t leave them as private because then my test cases can’t run them. So now I have to alter my code not to improve the application, but to make testing easier.

Obviously I could make the methods public, but that strikes me as a bad idea. Suddenly a bunch of internal methods that are really implementation details are part of the public interface of my class. From a purity standpoint, that’s a bad idea because someone else may come along and wonder what kind of API you’re providing. From a laziness standpoint, this approach stinks because when I reference the class in my IDE, I have to sift through all of those methods when I’m auto-completing a method name.

Protected scope is out, because unit tests usually subclass a generic test case, not the classes they’re testing.

I could leave out the scope entirely, leaving the methods with the default (package) scope. As long as I follow the practice of putting my tests in the same packages as the classes they test, this approach works, and is probably the right answer.

Is there another approach that’s recommended? Or do people generally just test against the public interfaces of their Java classes rather than testing the private methods that test bits of the business logic?

6 Comments

  1. If IncomeTaxReturnTest is in the same package as IncomeTaxReturn (regardless of the package of the test’s superclass), the test will be able to create instances of IncomeTaxReturn and invoke protected methods.

  2. I never knew that members of the same package could call protected methods.

  3. Another approach is to make them protected, then extend their class to create a special “ForTesting” version, so “IncomeTaxReturnForTesting” that has methods allowing you to access them.

  4. @Rafe: indeed. Check the Sun page on access control modifiers. If you do not give a function an access modifier, the default is package-private, which is private except for to the local package (which I think is what you’re looking for).

  5. I realize that this is a super old post, but it came up for me when looking into using package scope for unit testing.

    You did a great job verbalizing my internal thoughts on this. What stuck out to me was this line: “So now I have to alter my code not to improve the application, but to make testing easier.” That made me realize that this was the core of the issue. Really, the code/public API of the class should not be at the mercy of the tests. The tests just confirm that the input/output of the class does exactly what we expect it to do.

    By testing just parts of a class that are technically private to everyone else, the internals become more brittle when they really shouldn’t be. The internal implementation shouldn’t matter at all to any object that calls that class. That’s the whole point of hiding it from the outside world. If there’s parts inside the class that are really so complex that they need specialized tests for, maybe it would be better to make that functionality into a separate class and test it that way? If something is “untestable” (like a lot of my code is), I’m realizing that I need to either use dependency injection to provide mocks, or to make the code simpler so that it actually does only one thing.

    Thank you for the excellent post! It provided some insight into my own struggles to learn how to write more testable code.

Leave a Reply

Your email address will not be published.

*

© 2024 rc3.org

Theme by Anders NorenUp ↑