views:

2338

answers:

15

Hi all,

I'm new to doing serious unit testing as well as junit.

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.

Please let me know.

Thanks, jbu

A: 

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

Paul Tomblin
+3  A: 

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
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
+17  A: 

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.

MattK
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
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
+12  A: 

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
junit 3.8 and higher, you mean?
jbu
+3  A: 

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  A: 

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
+1  A: 

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
+3  A: 

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.

duffymo
A: 

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
A: 

This is not really posted as an answer, more that 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.

Steve B.
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
+1  A: 

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

}
Mongo
A: 

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
+1  A: 

See this similar stackoverflow question.

dhiller
A: 

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

Pierre Gardin
A: 

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