junit & java : testing non-public methods

JavaUnit TestingTestingJunit

Java Problem Overview


JUnit will only test those methods in my class that are public. How do I do junit testing on the ones that are not (i.e., private, protected)?

I can test them by not using junit, but I was wondering what the junit standard method was.

Java Solutions


Solution 1 - Java

One school of thought about unit testing says that you should only be able to test public methods, because you should only be unit-testing your public API, and that by doing so, you should be covering the code in your non-public methods. Your mileage may vary; I find that this is sometimes the case and sometimes not.

With that said, there are a couple of ways to test non-public methods:

  • You can test protected and package-scope methods by putting your unit tests in the same package as the classes they're testing. This is a fairly common practice.
  • You can test protected methods from unit tests in another package by creating a subclass of the class under test that overrides the methods you want to test as public, and having those overridden methods call the original methods with the super keyword. Typically, this "testing subclass" would be an inner class in the JUnit TestCase class doing the testing. This is a little bit more hacky, in my opinion, but I've done it.

Hope this helps.

Solution 2 - Java

As with many unit testing problems, testing private methods is actually a design problem in disguise. Rather than try to do anything tricky to test private methods, when I find myself wishing to write tests for private methods I take a minute to ask myself, "How would I need to design this so I could test it thoroughly through public methods?"

If that doesn't work, JUnitX allows testing private methods, although I believe it is only available for JUnit 3.8.

Solution 3 - Java

When you write a JUnit test, you have to do a subtle mind shift: "I'm a client of my own class now." That means private is private, and you only test the behavior that the client sees.

If the method really should be private, I'd consider it a design flaw to make it visible just for the sake of testing. You've got to be able to infer its correct operation based on what the client sees.

In the three years that have passed since I originally wrote this, I've started approaching the problem slightly differently, using Java reflection.

The dirty little secret is that you can test private methods in JUnit just as you would public ones, using reflection. You can test to your heart's content and still not expose them as public to clients.

Solution 4 - Java

The simplest solution is to put the JUnit tests in the same package (but different directory) and use default (i.e. package-private) visibility for the methods.

Another more complicated approach is to use reflection to access private methods.

Solution 5 - Java

If you have a significant amount of logic buried under relatively few "Public" entry points, you are probably violating the Single Responsibility Principle. If possible, you'll want to refactor the code into multiple classes, ultimately leading to more "Public" methods from which to test.

Solution 6 - Java

Here is the "probably shouldn't do it this way" method that everyone else keeps harping at you about. I think it's certainly within the realm of possibility that there are reasons for doing it this way, though. The following code will access a private field, but the code for a private method is nearly identical.

public void testPrivateField() throws InterruptedException {
    Class<ClassWPrivateField> clazz = ClassWPrivateField.class;
    try {
        Field privateField = clazz.getDeclaredField("nameOfPrivateField");
        privateField.setAccessible(true); // This is the line
        // do stuff
    } catch(NoSuchFieldException nsfe) {
        nsfe.printStackTrace();
        fail();
    } catch(IllegalAccessException iae) {
        iae.printStackTrace();
        fail();
    }
    
}

Solution 7 - Java

I nearly always use Spring in my Java projects and, as such, my objects are built for dependency injection. They tend to be fairly granular implementations of public interfaces that are assembled within the application context. As such, I rarely (if ever) have the need to test private methods because the class itself is small enough that it simply isn't an issue.

Even when I don't use Spring I tend to adopt the same practices of assembling small and simple objects into larger and larger abstractions, each of which is relatively simple but made complex by the aggregated objects.

In my experience, having the need to unit test private methods is an indicator that what you're teesting could (and should) be simplified.

That being, if you still really feel the need:

  • Protected methods can be tested by subclasses;
  • Package private methods can be tested by putting the unit tests in the same package; and
  • Private methods can be unit tested by providing, for example, a package private factory proxy method. Not ideal but private does mean private.

Solution 8 - Java

You usually don't test private methods because they can only (normally) be tested indirectly through another public method. When you're test driving and make private methods then they are usually a result of an "extract method" refactoring and are already by then tested indirectly.

If you are concerned about testing a private method with lots of logic then the smartest thing you could do is to move that code into another class in a public method. Once you've done that, the previous method that used this code can have it's testing simplified by having the functionality provided by a stub or a mock.

Solution 9 - Java

I've come across the same issue, and the "if it needs to be private it probably should be refactored" doesn't sit right with me.

Suppose you have sort of functionality that you want to separate out in some way internal to the class. For example, suppose I have something like this:

public class HolderOfSomeStrings{
  
    private List<String> internal_values;
  
    public List<String> get()
    {
       List<String> out = new ArrayList<String>();
       for (String s:internal_values)
       { 
           out.add(process(s));
       }
      return get;
    }

   private static String process(String input)
   {
     //do something complicated here that other classes shouldn't be interested in
    }

}

The point here is that junit forces me to make process public, or at least protected, or to put it in it's own utility class. But if it's some sort of internal logic of HolderOfSomeStrings, it's not at all clear to me that this is correct—it seems to me that this ought to be private, and making it more visible gums up the code in some way.

Solution 10 - Java

To borrow a thought from Andy Hunt, even your private methods must have some side effect that you're interested in. In other words, they must be called from some public method and perform an interesting task that causes the state of your object to change. Test for that state change.

Suppose you have public method pubMethod and private method privMethod. When you call pubMethod, it in turn calls privMethod to perform a task (perhaps parsing a String). The pubMethod then uses this parsed String to set member variables' values in some way or to influence its own return value. Test by watching for the desired effect on pubMethod's return value or on member variables (possibly by using accessors to get to them).

Solution 11 - Java

> DP4j Jar

For testing private methods we need to use reflection and its pointed in all answers.

well now this task is simplified with help of Dp4j jar.

  • Dp4j analyzes your code and automatically generates the Reflection API code for you.

  • Just add dp4j.jar to your CLASSPATH.

  • Dp4j.jar contains Annotation Processors, they will look for the methods in your code that are annotated with @Test JUnit annotation.

  • Dp4j analyze the code of those methods, and if it finds that you are illegally accessing private methods, it will replace your invalid private method reference with equivalent code that uses Java's Reflection API.

Get More details here

Solution 12 - Java

You can use TestNG instead of JUnit, which doesn't care about the method being private or public.

Solution 13 - Java

Use reflection as above to test private methods. If we are following TDD we should test private methods given that TDD implies that there would be no surprises later. So one should not wait to finish his public method to test privates. And this helps in more granular regression testing upon re factoring.

Solution 14 - Java

Look for "PrivateAccessor.invoke". My code imports it from "junitx.util", but I don't know where it came from.

Solution 15 - Java

In agreement with just about every post--you should probably refactor and probably not test private except through public, just wanted to add a different way to think about it...

Think of your class itself as a "Unit", not a method. You are testing the class and that it can maintain a valid state regardless of how it's public methods are called.

Calling private methods can destroy the encapsulation and actually invalidate the tests.

Solution 16 - Java

As kind of an offshoot of this, and I'm not sure where everyone comes down on the whole "polyglot programming" issue, but Groovy tests are runnable in Junit, and ignore the whole public/non-public issue. Side note, it was officially classified as a "bug", but when they tried to fix it, there was such a storm kicked up about it that it was put back as it was originally.

Solution 17 - Java

I absolutely agree with @duffymo that code should be tested from the client's view ( though he says he quit thinking this way). However, from this point of view, private vs others have different meanings. Client of a private method is the class itself so I prefer testing them through the external (public/package-protected) API. However, protected and package-protected members are there for external clients, so I test them with fakes which inherit the owning class or which reside in the same package.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjbuView Question on Stackoverflow
Solution 1 - JavaMattKView Answer on Stackoverflow
Solution 2 - JavaKent BeckView Answer on Stackoverflow
Solution 3 - JavaduffymoView Answer on Stackoverflow
Solution 4 - JavastarblueView Answer on Stackoverflow
Solution 5 - JavamarcumkaView Answer on Stackoverflow
Solution 6 - JavaCheese DaneishView Answer on Stackoverflow
Solution 7 - JavacletusView Answer on Stackoverflow
Solution 8 - JavaSpoikeView Answer on Stackoverflow
Solution 9 - JavaSteve B.View Answer on Stackoverflow
Solution 10 - JavaWil DoaneView Answer on Stackoverflow
Solution 11 - JavaVeKeView Answer on Stackoverflow
Solution 12 - JavaPierre GardinView Answer on Stackoverflow
Solution 13 - Javagyanendra kushwahaView Answer on Stackoverflow
Solution 14 - JavaPaul TomblinView Answer on Stackoverflow
Solution 15 - JavaBill KView Answer on Stackoverflow
Solution 16 - JavamezmoView Answer on Stackoverflow
Solution 17 - JavaCagatay KalanView Answer on Stackoverflow