10

There are different opinions on the meaningfulness of testing of private methods, e.g., here and here. I personally think it makes sense, the question is how to do it properly. In C++ you can use a #define hack or make the test class friend, in C# there's the InternalsVisibleToAttribute, but in Java we either have to use reflection or to make them "visible for testing" and annotate them as such in order to make the intent clear. The disadvantages of both should be quite clear.

I think there should be something better. Starting with

public class Something {
    private int internalSecret() {
        return 43;
    }
}

it would be nice to be able to call private methods in the test code like

@MakeVisibleForTesting Something something = new Something();
Assert.assertEquals(43, something.internalSecret());

Here the annotation would silently convert all calls to private methods of something using reflection. I wonder if Lombok could do it (and will ask the authors).

It's quite possible that doing that much magic proves too complicated, and in any case it'll take some time, so I'm looking for some alternative. Maybe annotating the class under test with something like @Decapsulate and using an annotation processor to generate a class Decapsulated_Something looking like

public class Decapsulated_Something {
    public Decapsulated_Something(Something delegate) {
        this.delegate = delegate
    }
    public boolean internalSecret() {
        // call "delegate.internalSecret()" using reflection
    }
    ...
}

which would allow to use

Decapsulated_Something something = new Decapsulated_Something(new Something());
Assert.assertEquals(43, something.internalSecret());

I don't have much experience with annotation processing, so I ask first here:

  • How complicated is this to implement?
  • What did I forget?
  • What do you think about it in general?
Community
  • 1
  • 1
maaartinus
  • 44,714
  • 32
  • 161
  • 320

5 Answers5

7

It seems like a lot of trouble to do this implementation. It may not be worth it. Rather just make the method package default.

However, if you are determined to call private method, you can use setAccessible in yourDecapsulated_something class to allow call via reflection. So it's fairly simple.

Alex Gitelman
  • 24,429
  • 7
  • 52
  • 49
2

There are number of approaches to take

  • Don't test private methods as they are hidden implementation details which should never make a difference to the caller.
  • Make the methods package local so a caller cannot access them, but you can access them in the same package i.e. a unit test.
  • Make the unit test an inner class or provide a package local inner class. Not sure this is an improvement!
  • Use reflection to access the methods of the class. This is like marking a method rpivate when its not and is a confusion IMHO. You should be only marking a method private when it is truely private.
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
2

it would be nice to be able to call private methods in the test code like

@MakeVisibleForTesting Something something = new Something();
Assert.assertEquals(43, something.internalSecret());

There's such thing as a method annotation, check out dp4j's @TestPrivates:

@Test
@TestPrivates 
//since the method is annotated with JUnit's @Test this annotation is redundant. 
// You just need to have dp4j on the classpath.
    public void somethingTest(){
      Something something = new Something();
      int sthSecret = something.internalSecret();
       Assert.assertEquals(43, sthSecret); //cannot use something.internalSecret() directly because of bug [dp4j-13][2] 
    }
simpatico
  • 10,709
  • 20
  • 81
  • 126
1

I'll answer the "In general" question :-) It only takes a few lines of code to make a method accessible via reflection and there are quite a number of libraries, utils, APIs etc that provide methods for doing so. There's also probably many different techniques you could use in your own code. For example bytecode manipulation, reflection, class extensions, etc. But I'd be inclined to keep things simple. Whilst it can be useful to test private methods, it's also likely that you will only want to test a few. So engineering something complex is probably overkill. I'd just use an established API, or write a quick method to access the private methods I was interested in and let it be done at that.

drekka
  • 20,957
  • 14
  • 79
  • 135
1

I worked on a project a few years back that generated classes to make it easier to unit test private methods. http://java.net/projects/privateer/

It generated extra classes that made it easier than calling reflection, e.g. if you had MyClass.myPrivateMethod() it would generate a _MyClass class that would allow invocation of myPrivateMethod directly.

It was never really finished and was kind of useful for a few cases, but overall I wouldn't recommend testing private methods unless absolutely necessary. Usually redesigning them into utility classes (with package access if you're worried about users using them) is a better option.

prunge
  • 22,460
  • 3
  • 73
  • 80