When it comes to unit testing you might find yourself wanting to test private methods. Here’s four solutions, some much better than others.
1. Don’t test private methods (refactor!)
If you find yourself needing to test private methods, you’re code is trying to tell you something – listen up!
Unit tests should test the behavior of classes and not the implementation details. Unit tests should be able to naturally cover your private methods. If you find this difficult or impossible to do, consider it a code smell.
Testing implementation will become a barrier to refactoring since you will be unable to change the implementation without updating tests. As Charles Miller put it “The amount of work you have to do to improve your code becomes the amount needed to change the private methods, _plus_ that required to change the tests. As such, you’re less likely to make the improvement.”
If you choose to test private methods keep in mind you are likely taking on some technical debt.
Pros: You’re not testing private methods. Best solution. Results in better design.
Cons: Can sometimes be tricky to implement, especially in legacy applications.
2. Use reflection
Thanks to reflection, access levels are more of a suggestion than a requirement. With a little bit of code, we can access any private method (or field for that matter).
First use reflection to change the access level:
MySuperCoolDao mySuperCoolDao = new MySuperCoolDao();
// get method by name "isThisCool" and signature
Method isThisCoolMethod = mySuperCoolDao.getClass().
getDeclaredMethod( "isThisCool", String.class );
// make method accessible
getCoordiantesMethod.setAccessible( true );
Then we use the newly accessible method:
boolean isWindowsCool = isThisCoolMethod.invoke(mySuperCoolDao, "Windows");
boolean isUbuntuCool = isThisCoolMethod.invoke(mySuperCoolDao, "Ubuntu");
assertFalse("Windows is not cool…", isWindowsCool);
assertTrue("Ubuntu is very cool", isUbuntuCool);
A big negative to this method is maintainability. In fact this just happened to me today: I refactored out a private method and as expected the test failed. Unlike other solutions (like the package level solution below), the failure was at runtime – not compile time.
Pros: Relatively easy and quick. Does not break encapsulation.
Cons: Tests become busier. More difficult to keep tests updated with production code. Security settings could prevent this technique from working.
3. Use package level access
This solution is very easy, simply remove private to get the (default) package level access. As long as your tests are in the same package as the production code, no problem. Since it’s good practice for your unit tests to be in the same package (but in a separate source folder), this works well.
Right out of college, this was the first way I learned to handle these situations. Almost everything method was package level. It never really smelled quite right, but the postives out weighed the negatives. In hindsight it might not have been the best solution, but there are far worse things.
Pros: Quick and easy.
Cons: Breaks encapsulation. Classes become a little noisier (although not as bad as using public).
4. Mix production and test code
This is the worst of all the solutions, so I hesitate to even bring it up. By making your test class an inner class in your production code, your tests will have access to private methods. Unfortunately this solution leaves your production code dependent on your test code.
Challenge: is there a legitimate reason to do this? I can’t think of one.
Pros: At least you’re writing tests! Weaksauce, I know.
Cons: Production code contains more than production code.
Quick update: Chad Bradley informed me that it is possible to test private methods using Groovy. I would be hesitant to use the technique without knowing for certain if it’s a bug that will get fixed or an actual language feature.