I am working on a java project. I am new to unit testing. What is the best way to unit test private methods in java classes?
- Anybody can ask a question
- Anybody can answer
- The best answers are voted up and rise to the top
You generally don't unit test private methods directly. Since they are private, consider them an implementation detail. Nobody is ever going to call one of them and expect it to work a particular way.
You should instead test your public interface. If the methods that call your private methods are working as you expect, you then assume by extension that your private methods are working correctly.
In general, I would avoid it. If your private method is so complex that it needs a separate unit test, it often means that it deserved its own class. This may encourage you to write it in a way which is reusable. You should then test the new class and call the public interface of it in your old class.
On the other hand, sometimes factoring out the implementation details into separate classes leads to classes with complex interfaces, lots of data passing between the old and new class, or to a design which may look good from the OOP point of view, but does not match the intuitions coming from the problem domain (e.g. splitting a pricing model into two pieces just to avoid testing private methods is not very intuitive and may lead to problems later on when maintaining/extending the code). You don't want to have "twin classes" which are always changed together.
When faced with a choice between encapsulation and testability, I'd rather go for the second. It's more important to have the correct code (i.e. produce the correct output) than a nice OOP design which doesn't work correctly, because it wasn't tested adequately. In Java, you can simply give the method "default" access and put the unit test in the same package. Unit tests are simply part of the package you're developing, and it's OK to have a dependency between the tests and the code which is being tested. It means that when you change the implementation, you may need to change your tests, but that's OK -- each change of the implementation requires re-testing the code, and if the tests need to be modified to do that, then you just do it.
In general, a class may be offering more than one interface. There is an interface for the users, and an interface for the maintainers. The second one can expose more to ensure that the code is adequately tested. It doesn't have to be a unit test on a private method -- it could be, for example, logging. Logging also "breaks encapsulation", but we still do it, because it's so useful.
Testing of private methods would depend on their complexity; some one line private methods wouldn't really warrant the extra effort of testing (this can also be said of public methods), but some private methods can be just as complex as public methods, and difficult to test through the public interface.
My preferred technique is to make the private method package private, which will allow access to a unit test in the same package but it will still be encapsulated from all other code. This will give the advantage of testing the private method logic directly instead of having to rely on a public method test to cover all parts of (possibly) complex logic.
If this is paired with the @VisibleForTesting annotation in the Google Guava library, you are clearly marking this package private method as visible for testing only and as such, it shouldn't be called by any other classes.
Opponents of this technique argue that this will break encapsulation and open private methods to code in the same package. While I agree that this breaks encapsulation and does open private code to other classes, I argue that testing complex logic is more important than strict encapsulation and not using package private methods which are clearly marked as visible for testing only must be the responsibility of the developers using and changing the code base.
Private method before testing:
Package private method ready for testing:
Note: Putting tests in the same package is not equivalent to putting them in the same physical folder. Separating your main code and test code into separate physical folder structures is good practice in general but this technique will work as long as the classes are defined as in the same package.
Unit test case means testing the unit of code. It does not mean testing the interface because if you are testing the interface, that does not mean you are testing the unit of code. It becomes kind of a black box testing. Also, it is better to find issues on the smallest unit level than determining the issues on the interface level and then trying to debug that which piece was not working. Therefore, unit test case should be tested irrespective of their scope. Following is a way to test private methods.
If you are using java, you can use jmockit which provides Deencapsulation.invoke to call any private method of the class under testing. It uses reflection to call it eventually but provides a nice wrapper around it. (https://code.google.com/p/jmockit/)
First of all, as other authors suggested: think twice if you do really need to test private method. And if so, ...
In .NET you can convert it into "Internal" method, and make package "InternalVisible" to your unit test project.
In Java you can write tests itself in the class to be tested and your test methods should be able to call private methods as well. I don't really have big Java experience, so that's probably not the best practice.
If you cannot use external APIs or dont want to, you can still use pure standard JDK API to access private methods using reflection. Here is an example
Check Java Tutorial http://docs.oracle.com/javase/tutorial/reflect/ or Java API http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary.html for more information.
As @kij cited on his answer there are times when a simple solution using reflection is really good to test a private method.
I usually make such methods protected. Let's say your class is in:
You can create a test class as:
Now you have access to protected methods and can unit test them (JUnit or TestNG doesnt really matter), yet you keep these methods from callers you did not want.
Note that this expects a maven-style source tree.
If you really need to test private method, with Java I mean, you can use
Import the library with maven (given versions are not the lastest I think) or import it directly in your classpath:
As an example, if you have a class named 'MyClass' with a private method named 'myPrivateMethod' which take a String as parameter an update its value to 'this is cool testing !', you can do the following junit test:
This library also enables you to replace any bean properties (no matter they are private and no setters are written) by a mock, and using this with Mockito or any other mock framework is really cool. The only thing you have to know at the moment (don't know if this will be better in next versions) is the name of the target field / method you want to manipulate, and its signature.
What I normally do in C# is make my methods protected and not private. It's a slightly less private access modifier, but it hides the method from all classes that do not inherit from the class under test.
Any class that doesn't inherit directly from classUnderTest has no idea that methodToTest even exists. In my test code, I can create a special testing class that extends and provides access to this method...
This class only exists in my testing project. Its sole purpose is to provide access to this single method. It allows me to access places that most other classes do not have.
You can test private methods easily if you put your unit tests in an inner class on the class you are testing. Using TestNG your unit tests must be public static inner classes annotated with @Test, like this:
Since it's an inner class, the private method can be called.
My tests are run from maven and it automatically finds these test cases. If you just want to test one class you can do
protected by gnat May 28 '15 at 10:36
Thank you for your interest in this question.
Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).
Would you like to answer one of these unanswered questions instead?