How to Test a Class That Has Private Methods, Fields or Inner Classes

How do I test a class that has private methods, fields or inner classes?

If you have somewhat of a legacy Java application, and you're not allowed to change the visibility of your methods, the best way to test private methods is to use reflection.

Internally we're using helpers to get/set private and private static variables as well as invoke private and private static methods. The following patterns will let you do pretty much anything related to the private methods and fields. Of course, you can't change private static final variables through reflection.

Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

And for fields:

Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);


Notes:

  1. TargetClass.getDeclaredMethod(methodName, argClasses) lets you look into private methods. The same thing applies for
    getDeclaredField.
  2. The setAccessible(true) is required to play around with privates.

How do I test a class that has private methods, fields or inner classes?

If you have somewhat of a legacy Java application, and you're not allowed to change the visibility of your methods, the best way to test private methods is to use reflection.

Internally we're using helpers to get/set private and private static variables as well as invoke private and private static methods. The following patterns will let you do pretty much anything related to the private methods and fields. Of course, you can't change private static final variables through reflection.

Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

And for fields:

Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);


Notes:

  1. TargetClass.getDeclaredMethod(methodName, argClasses) lets you look into private methods. The same thing applies for
    getDeclaredField.
  2. The setAccessible(true) is required to play around with privates.

Unit test private inner class methods

since the cache need not be visible to external consumers

A unit test is an external consumer. It's a class which invokes functionality on the object being tested, just like any other class.

Caveat: There is much opinion and debate on this matter. What I'm presenting here isn't "the one true answer" but is based on my own experience in maintaining unit tests in code.

Don't directly unit test private members. Not only does it generally take a little bit of trickery to make that happen, it creates coupling between the classes. (The test class and the class being tested.) Exposing the internals and coupling to them is a violation of object oriented principles.

Rather than thinking of your tests in terms of the methods you call on the class, think of your tests in terms of the functionality you invoke on the unit. Whatever functionality that unit exposes is what should be tested.

This leads to a few conclusions:

  • If there is no public functionality which internally invokes the private members in question, then why are those private members there at all? Just remove them.
  • If the private functionality is very complex and very difficult to invoke/validate using only public functionality then perhaps some refactoring is in order to simplify the class.

Since code which uses the object can only invoke the public functionality, code which tests the object should only validate the public functionality.

Java test class with many private methods

In theory, your private methods are being used ultimately by one of the public methods, or else they're not used at all. So typically you setup your tests to call the public methods with the necessary context so that it hits your private methods.

The unit tests are primarily testing the compilation unit (i.e. the class). You can unit test methods directly but then they have to be public, which goes against having a nice clean API.

So test your public method enough to hit all the private methods. Private methods are internal mechanics of the class, they don't need to be tested directly.

Junit Test Case for private method

First of all a couple of things:

  1. Why is loading_status static? Basically 2 instances of this class would use the same loading_status variable. loading_status should either not be static or checkLoadingStatus should be static as well.
  2. loading_status is terribly named. If you want to keep it static, you should name it LOADING_STATUS. If it won't be static, let's name it loadingStatus. That's the Java convention.
  3. You should create a proper enum type for loading_status instead of it being an integer.
  4. while (loading_status == IN_PROGRESS); is just simply bad practice. At least what you can do is: while(loading_status == IN_PROGRESS) { Thread.sleep(100); }; However a timeout is a better practice. Sometehing like:
long timeoutMillis = 5000L; // Should come from a configuration or something
long endTimeMillis = System.currentTimeMillis() + timeoutMillis;
while (loadingStatus == IN_PROGRESS) {
long remainingMillis = end - System.currentTimeMillis();
if (remaining <= 0) {
// Timeout while loading
loadingStatus = LOADING_ERROR;
break;
}
Thread.sleep(50);
}
// ...

And finally I guess something will update loadingStatus to completed or something.
I guess you are using synchronized there as well.
If the loading happends on 2 different threads then you will end-up in a dead-lock.
If checkLoadingStatus enter the synchronized block first, so when the loading is completed the loading thread will never be able to enter the synchronized block, because the checkLoadingStatus is holidng the lock.

Last but not least to answer you question, if you want to keep your method private, you can invoke it through reflection, but that's again bad practice.
Generally you should avoid unit testing private methods and unit test those methods which are invoking it.
If however you definitely need to unit test a particular method, make it package-private instead of private, and then you can create a unit test in the same package where the class is which contains your method. And you can add a comment to the method saying it's only visible for unit testing. Example of your code structure in this case:

src
+-- main
+-- +-- com/app/MyClass.java
+-- test
+-- com/app/MyClassTest.java // this test class will able to access package-private methods in MyClass

How to test private method of a private static class

Assuming your class ProjectModel is in package privateaccessor.tst and your non-static method model returns an int.

package privateaccessor.tst;
public class ProjectModel {
//some code
private static class MyStaticClass{
private int model (Object obj, Map<String , Object> model) {
return 42;
}
}
}

Then in your test you can use reflection to get the private class' Class object and create an instance. Then you can use the PrivateAccessor (contained in Junit Addons) to call method model().

@Test
public void testPrivate() throws Throwable {
final Class clazz = Class.forName("privateaccessor.tst.ProjectModel$MyStaticClass");
// Get the private constructor ...
final Constructor constructor = clazz.getDeclaredConstructor();
// ... and make it accessible.
constructor.setAccessible(true);
// Then create an instance of class MyStaticClass.
final Object instance = constructor.newInstance();
// Invoke method model(). The primitive int return value will be
// wrapped in an Integer.
final Integer result = (Integer) PrivateAccessor.invoke(instance, "model", new Class[]{Object.class, Map.class}, new Object[]{null, null});
assertEquals(42, result.intValue());
}

Use Reflection to Write test for private methods

But the question is how much is this way okay to write test for private methods with using of reflection?

Is there a better way to achieve this goal?

The most common approach is to abandon that goal.

Historically, the focus of TDD has been on verifying behaviors, not implementation details. Part of the point was that the tests give us the freedom to change the internal design of the code, confidently, because the tests will alert us to any unintended changes in behavior.

So if we need a test to ensure that the logic in some private method is correct, we find a use case for the public API that depends on that logic, and write tests that measure the behavior of the public API.

We can calibrate the test by injecting a temporary fault into the private method, and verifying that the test detects the fault.

Having done that, we have the option of refactoring the code by inlining the private method; all of the tests should still pass in that case, because we haven't changed the behavior of the test subject -- we've just moved the details around.

If you are going to be aggressively working to improve the internal design of your application, then you need tests that are decoupled from that internal design.



Related Topics



Leave a reply



Submit