Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java tool for testing private methods?

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?
like image 726
maaartinus Avatar asked Aug 16 '11 05:08

maaartinus


3 Answers

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.

like image 150
Alex Gitelman Avatar answered Oct 09 '22 14:10

Alex Gitelman


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] 
    }
like image 45
simpatico Avatar answered Oct 09 '22 14:10

simpatico


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.
like image 43
Peter Lawrey Avatar answered Oct 09 '22 16:10

Peter Lawrey