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?
-
2Check this question on StackOverflow. A couple of techniques are mentioned and discussed. What's the best way of unit testing private methods?– ChironCommented Aug 14, 2011 at 5:02
-
45My opinion has always been that private methods don't need testing as you should be testing what is available. A public method. If you can't break the public method does it really matter what the private methods are doing?– rlperezCommented Apr 12, 2012 at 18:22
-
3Both public and private methods should be tested. Hence, a test driver generally needs to be inside the class it tests. like this.– overexchangeCommented May 28, 2015 at 10:08
-
3@Rig -- +1 -- you should be able to invoke all the required behavior of a private method from your public methods -- if you cannot then the functionality can never be invoked anyway so there is no point in testing it.– James AndersonCommented May 28, 2015 at 10:40
-
1This article was useful for me enterprisecraftsmanship.com/posts/unit-testing-private-methods– frederjCommented Feb 24, 2020 at 18:28
14 Answers
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.
-
52+1 bazillion. And if a private method is never called, don't unit-test it, delete it! Commented Aug 14, 2011 at 6:08
-
391I disagree. Sometimes a private method is just an implementation detail, but it still is complex enough that it warrants testing, to make sure it works right. The public interface may be offering a too high level of abstraction to write a test which directly targets this particular algorithm. It's not always feasible to factor it out into a separate class, for due to the shared data. In this case I'd say it's OK to test a private method. Commented Aug 14, 2011 at 8:04
-
50@quant_dev: Sometimes a class needs to be refactored into several other classes. Your shared data can be pulled into a separate class too. Say, a "context" class. Then your two new classes can refer to the context class for their shared data. This may seem unreasonable, but if your private methods are complex enough to need individual testing, it's a code smell that indicates your object graph needs to become a little more granular.– PhilCommented Jul 17, 2012 at 14:47
-
117This answer DOES NOT answer the question. The question is how should private methods be tested, not whether they should be tested. Whether a private method should be unit tested is an interesting question and worthy of debate, but not here. The appropriate response is add a comment in the question stating that testing private methods may not be a good idea and give a link to a separate question which would go into the matter more deeply.– TallGuyCommented Jan 30, 2014 at 22:39
-
21@TallGuy Allow me to distill my answer here to its core point. The answer to "how does one unit test private methods?" is "One doesn't."– Adam Lear ♦Commented Jan 30, 2014 at 22:40
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.
-
19+1 Interesting point. In the end it's about maintainability, not adherence to the "rules" of good OOP.– PhilCommented Jul 17, 2012 at 14:55
-
14+1 I completely agree with testability over encapsulation.– RichardCommented Nov 27, 2012 at 10:44
-
-
I've added an answer that is more lightweight: introducing a static method. I guess that if you go the route of adding a class then you might want to look into e.g. the Strategy design pattern. Commented May 16, 2023 at 15:02
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:
private int add(int a, int b){
return a + b;
}
Package private method ready for testing:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
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.
-
1
-
@VisibleForTesting
is also inandroidx.annotation
, which comes with just about every AndroidX library. Commented Apr 6, 2020 at 15:39 -
but why cannot the method call cannot compile saying it's private?– Lei YangCommented Sep 21, 2020 at 6:41
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
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
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.
-
1This seems highly inapprobiate. How do you handle automatic refactoring in your 116 tests now? manually? Commented Nov 12, 2019 at 10:13
-
@Wisienkas Use an (at)see tag in the javadoc for your unit test, when the automatic refactoring updates, it will update the javadoc - the test will fail and the answer will be locally apparent. Ideally the dev would review their commit (diff) prior and update the string that was missed (near the changed javadoc). Commented Apr 2, 2020 at 13:21
-
This approach never gets old, right? Is this approach still legit? Commented May 27, 2020 at 11:07
I usually make such methods protected. Let's say your class is in:
src/main/java/you/Class.java
You can create a test class as:
src/test/java/you/TestClass.java
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.
-
This is a bad answer IMHO;
protected
is more visible than the default package visibility. Why would you ever include subclasses while you just need visibility for unit testing? Commented May 16, 2023 at 14:38
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/)
-
-
@gnat, The question was that What is the best way to unit test private methods in java classes? And my first point answers the question. Commented Feb 5, 2014 at 19:46
-
1your first point merely advertises a tool, but it doesn't explain why you believe it is better than alternatives, such as eg testing private methods indirectly as was suggested and explained in top answer– gnatCommented Feb 5, 2014 at 19:49
-
jmockit has moved and the old official page points to an article about "Synthetic Urine and Artificial Pee". Wich is by the way still related to mocking stuff, but not relevant here :) The new official page is: jmockit.github.io Commented Aug 19, 2019 at 12:12
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.
Thanks.
This section from the book Clean Code, in Chapter 10 Classes > Class Organization > Encapsulation kinda implies that testing on private method is a pseudo-proposition.
We like to keep our variables and utility functions
private
, but we're not fanatic about it. Sometimes we need to make a variable or utility functionsprotected
so that it can be accessed by a test. For us, tests rule. If a test in the same package needs to call a function or access a variable, we'll make itprotected
or package scope. However, we'll first look for a way to maintain privacy. Loosening encapsulation is always the last resort.
IMHO private
method is just a wrapper of a partial responsibility implementation, which is invoked in non-private method. So if testing on a private
method is very important, the access scope should be enlarged so that a unit test framework like JUnit is able to run test on it. Otherwise, the only way to "test" private
method is in fact the test on a non-private
method, which calls that private
method
Moreover, the Deencapsultion class of JMockit was removed last year. This kinda proved that "test on private method" is a pseudo-proposition
If you really need to test private method, with Java I mean, you can use fest assert
and/or fest reflect
. It uses reflection.
Import the library with maven (given versions are not the lastest I think) or import it directly in your classpath:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
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:
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
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.
-
2While reflection will enable you to test private methods, it is not good for detecting uses of them. If you change the name of a private method, this will break your test.– RichardCommented Nov 27, 2012 at 10:42
-
1The test will break yes, but this is a minor issue from my point of view.Of course i fully agree about your explanation below on the package visibility (with tests classes in seperate folders but with same packages), but sometimes you don't really have the choice. For example if you have a really short mission in an enterprise which doesn't apply "good" methods (test classes not in the same package, etc.), if you don't have time to refactor existing code on which you are working / testing, this is still an good alternative.– kijCommented Nov 27, 2012 at 11:14
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.
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
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...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
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.
-
4
protected
is part of the public API and incurs the same limitations (can never be changed, must be documented,...). In C# you can useinternal
together withInternalsVisibleTo
. Commented Dec 13, 2013 at 11:03
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:
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
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
$ mvn test -Dtest=*UserEditorTest
Source: https://rogerkeays.com/how-to-unit-test-private-methods
-
4You are suggesting including test code (and additional inner classes) in the actual code of the package that is deployed?– user40980Commented Apr 11, 2013 at 3:36
-
1The test class is an inner class of the class you want to test. If you don't want those inner classes to be deployed you can just delete the *Test.class files from your package. Commented Apr 11, 2013 at 7:49
-
1I don't love this option, but for many it's probably a valid solution, not worth the downvotes.– Bill KCommented May 8, 2013 at 17:15
-
@RogerKeays: Technically when you do a deployment, how do you remove such "test inner classes"? Please don't say you cut them by hand. Commented Dec 13, 2013 at 10:54
-
I don't remove them. Yes. I deploy code that is never executed at run time. It really is that bad. Commented Dec 15, 2013 at 1:14
In Swift or Objective-C, you mark the method as @testable. The compiler knows when it compiles unit tests (because the end product is not an application or library but a "unit test" executable), and in that case allows calling private methods marked as @testable as if they were public. The advantage of this system is that your methods stay as private as you want them except for unit tests (where normal rules don't apply) and you don't need any code changes or preprocessor tricks or similar to support this.
I think other languages have similar features.
As another answer mentions, you can use a nested class, but I want to explain why you might want to do this.
For logic code that has been made dependency-free (ie: not UI code, not database code, etc.), theoretically you should not do this. Dependency-free code is basically an API, and you "should" be able to hit all of the code through that API, even the private stuff. If you can't access some of the private stuff through the public interface, then what is that code even doing?
However, for UI and other kinds of presentation code (reports), this is not the case. A part of the "interface" of this type of code is the user's physical interface with the computer. This means that fully encapsulated UI code cannot be directly tested programmatically, because it is not a programmatic interface.
I have found, at least so far, that a clean approach that allows testing this kind of code is to use a nested class. Create a "Tester" class nested within the class you want to test that takes a reference to an instance of that class. Then, as needed, write some simple methods that will tell you things about the state of the UI.
There are two extremes that I would suggest avoiding. First, don't put the unit tests themselves into the nested Tester class. Second, don't just expose the internals of the class through the Tester. Use the Tester to give you simple but meaningful answers about questions you have about the UI, or possibly instructions about how to manipulate the UI. Basically these methods will be a kind of replacement/stand-in for the user.
(Note that two things make this a little cleaner in C# than in Java. C# has conditional compilation, so you can wrap your Tester class in #if DEBUG
and leave it out of your release version. Also, C# has partial classes, so you can put nested classes in their own files.)
Here is a rough pseudo-Java example since I don't really do this in Java:
(note: in my UI code I use the convention that UI fields get a 'c' prefix)
public class SomethingWindow extends SomeUIClass {
final TextBox cTextBox1;
final TextBox cTextBox2;
final ListBox<Item> cListBox;
final CheckBox cHideSomeBox;
...
public class Tester {
final SomethingWindow mWindow;
public Tester(SomethingWindow window) {
mWindow = window;
}
public void HideSome() { mWindow.cHideSomeBox.Checked = true; }
public Item[] GetLoadedItems() { return mWindow.cListBox.GetItems(); }
public void SetTextBox1Text(String text) { mWindow.cTextBox1.Text = text; }
public String GetTextBox2Text() { return mWindow.cTextBox2.Text; }
public boolean GetTextBox2IsReadOnly() { return mWindow.cTextBox2.ReadOnly; }
}
}
And your unit tests might look something like this:
@Test
public void Test_HidingSomeWorks() {
var context = new TestContext();
// add stuff to context or something
var window = new SomethingWindow(context);
window.Init();
var tester = new SomethingWindow.Tester(window);
tester.HideSome();
var result = tester.GetLoadedItems();
// assert that the expected items have been returned
// ie: that the right ones have been filtered away
}
@Test
public void Test_RelatedTextBoxes() {
var context = new TestContext();
var window = new SomethingWindow(context);
var tester = new SomethingWindow.Tester(window);
tester.SetTextBox1Text("some text");
var result = tester.GetTextBox2Text();
// assert something about the result
}
-
Your answer starts with a lot of discussion, without getting to the point. I'd change the order or at least create a TL;DR. I've certainly lost interest after the first three sections. Commented May 16, 2023 at 13:37
You could perform the calculations in a a separate, more visible static
method. As the static method doesn't contain or alter any state directly, it is safe for any method to call it.
Say you have the following class:
class ClassToTest {
private int state1;
private int state2;
private int state3;
private void changeState1() {
// imagine that the addition is a hard problem that needs to be tested
state1 += state2;
}
}
Then you can create a method with a higher visibility by simply forwarding it the relevant state:
class ClassToTestRefactorUsingState {
private int state1;
private int state2;
private int state3;
private void changeState1() {
state1 = calculateNewState(state1, state2);
}
// testable method taking state as variables
static int calculateNewState(int state1, int state2) {
// same hard problem
return state1 + state2;
}
}
If there is a lot of state then it might be useful to pass the instance instead:
class ClassToTestRefactorUsingInstance {
private int state1;
private int state2;
private int state3;
private void changeState1() {
state1 = calculateNewState(this);
}
// testable method taking an instance, usable if a lot of state is affected
static int calculateNewState(ClassToTestRefactorUsingInstance instance) {
// same hard problem
return instance.state1 + instance.state2;
}
}
Beware that it might still break encapsulation if the method has side effects outside changing the instance fields. For example, if it changes a file created by the instance then encapsulation may still be broken.
Notes:
One way to abstract the problem away from the method has already been included in answer(s): use a separate class that contains the required functionality (e.g. using the Strategy design pattern). This may however result in a very complex class design, and you'd be testing functionality that is (presumably) directly related to this class within a different class.
Unfortunately this method doesn't work as well if a lot of fields have to be altered (Java allows for many parameters, but only one return value after all). You could create a local local
Result
class, but if that's the case a larger refactor may be required. In all probability your state is getting too large, and the state may have to be regrouped into multiple classes.If you are overly worried you may retain a method that makes sure that the state is at least altered by the original method, without testing any edge cases and such.
If the method is too tightly integrated you may have to bite the bullet and just test the visible methods. You could create additional visible read-only methods to validate that the calculation has been correctly performed and / or that the resulting state is still valid.
If an input parameter of the static method (in this case
calculateNewState
) is a mutable object then additional care should be taken that side effects don't alter the state of the program or testing.