109

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.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
jbu
  • 15,831
  • 29
  • 82
  • 105
  • 1
    Use **`@Jailbreak`** from the Manifold framework to directly and **type-safely** access private methods and fields. (https://medium.com/@scott_86456/jailbreak-with-manifold-4397700eda97) – Scott Mar 15 '19 at 20:15

17 Answers17

94

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.
starball
  • 20,030
  • 7
  • 43
  • 238
MattK
  • 10,195
  • 1
  • 32
  • 41
  • 9
    No subclassing required for protected actually, if your class under test is in src/box/of/Cats.java, in your test project put the test class to src/box/of/CatsTest.java and link the projects. That way your test classes seem to be in same package and thus can access each other's protected methods. – Esko Jan 14 '09 at 07:22
  • 6
    The method of subclassing in order to access protected members was proposed to conform to the (generally) accepted practice of placing your test and source code in *different* packages. This makes it easy to specify to exclude your unit tests in the final distribution in build automation tools. – Dinuk Jun 29 '09 at 23:03
  • 17
    @Dinuk: Note that instead of placing test and source code in different packages, you can also place them into different source code trees. This is how e.g. Maven does it (src/main/java and src/test/java ). That way implementation class & test class can be in the same package, but are easily treated separately for build/deployment. – sleske Jul 30 '12 at 13:36
  • @Esko: I think you meant your method is intended for "package-accessible methods", not "protected" – teddy teddy Dec 20 '12 at 06:46
  • @teddyteddy Well my comment is almost four years old so I can claim that I used to be a bit less skillful back then :) - But yes, that would be the more semantically right way of saying that. – Esko Dec 22 '12 at 07:56
  • @teddy_teddy & @Esko: since `protected` is **less** restrictive than the default "package" accessibility modifier, the method described by @Esko will work for both, but ideally it should be done in the "Maven way" noted by @sleske – charlie Jan 17 '14 at 12:59
40

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.

Kent Beck
  • 5,011
  • 2
  • 28
  • 31
  • junit 3.8 and higher, you mean? – jbu Jan 13 '09 at 21:11
  • 3
    Testing private methods is useful for test-driven development. The tests should not be run as part of the main test system (allowing for changes to the implementation without breaking a build), but they can be very useful for specifying the desired behavior. – Quantum7 Nov 10 '11 at 01:19
  • @Quantum7 You could do this through Temporary detailed testing supprting refactoring, see http://c2.com/cgi-bin/wiki?TemporaryDetailedTestingSupportingRefactoring – Matthew Farwell Nov 17 '11 at 15:21
26

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.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • would you mind providing a complete example of testing a private method using Java reflection? i've often made private methods protected for the sake of testing, which is not something i like to do. that being said, i do not agree at all with the assertion that private methods should be tested implicitly by calling a public method that will eventually delegate to the method to be tested. this *is not* what **unit** testing is supposed to be; unit testing tests atomic pieces of code (i.e., a unit test tests each method discretely). – liltitus27 Jan 08 '14 at 15:39
  • So if you don't agree at all, why bother with an example? Just don't do it. – duffymo Jan 08 '14 at 15:40
  • well, i don't want to implicitly test low-visibility methods, i want to test them explicitly, which i inferred was possible using java reflection, as you suggested. and oftentimes, i like to try out and test things that i don't initially agree with; it helps me to understand why i think what i think, and also why others think the way they think. i wasn't trying to lambaste you, i was just adding my thoughts to the mix. – liltitus27 Jan 08 '14 at 15:45
  • I'm not sure that it's adding value to a question that was answered five years ago. – duffymo Jan 08 '14 at 18:19
9

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.

starblue
  • 55,348
  • 14
  • 97
  • 151
  • 1
    This is a very pragmatic solution if you don't have the luxury of working with a code base in which every class obeys the SRP and every other design guideline. – Andrew Swan Jan 13 '09 at 21:49
9

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.

marcumka
  • 1,695
  • 3
  • 12
  • 14
8

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();
    }

}
Cheese Daneish
  • 1,030
  • 7
  • 15
  • This tests private fields, but you can also use reflection to test private methods (clazz.getDeclaredMethod). This is sometimes useful when you really want the method private rather than package/protected. – Quantum7 Nov 10 '11 at 01:14
4

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.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Steve B.
  • 55,454
  • 12
  • 93
  • 132
  • 7
    The testing philosophy - if all of your public methods do what they're meant to, it doesn't matter *at all* how they are implemented internally. Conversely if the helper method is something you want to test to make safe for reuse, make it public and put it in a utility class. – Andrzej Doyle Jan 13 '09 at 22:37
3

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.
cletus
  • 616,129
  • 168
  • 910
  • 942
3

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.

Spoike
  • 119,724
  • 44
  • 140
  • 158
2

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

VedantK
  • 9,728
  • 7
  • 66
  • 71
2

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).

Wil Doane
  • 154
  • 2
1

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.

1

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

Pierre Gardin
  • 684
  • 1
  • 8
  • 23
0

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.

mezmo
  • 2,441
  • 19
  • 22
0

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

Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
0

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.

Bill K
  • 62,186
  • 18
  • 105
  • 157
0

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.

Cagatay Kalan
  • 4,066
  • 1
  • 30
  • 23