ChatGPT解决这个技术问题 Extra ChatGPT

annotation to make a private method public only for test classes [duplicate]

This question already has answers here: How do I test a class that has private methods, fields or inner classes? (58 answers) Closed 4 years ago.

Who has a solution for that common need.

I have a class in my application.

some methods are public, as they are part of the api, and some are private, as they for internal use of making the internal flow more readable

now, say I want to write a unit test, or more like an integration test, which will be located in a different package, which will be allowed to call this method, BUT, I want that normal calling to this method will not be allowed if you try to call it from classes of the application itself

so, I was thinking about something like that

public class MyClass {

   public void somePublicMethod() {
    ....
   }

   @PublicForTests
   private void somePrivateMethod() {
    ....
   }
}

The annotation above will mark the private method as "public for tests" which means, that compilation and runtime will be allowed for any class which is under the test... package , while compilation and\or runtime will fail for any class which is not under the test package.

any thoughts? is there an annotation like this? is there a better way to do this?

it seems that the more unit tests you write, to more your inforced to break your encapsulation...

why not use reflection to access private method?
@Scott When advertising your own programs, "[Y]ou must disclose your affiliation in your answers." (stackoverflow.com/help/behavior). Not questioning the Manifold framework per se, just the subtle way you are promoting it.

N
Naman

The common way is to make the private method protected or package-private and to put the unit test for this method in the same package as the class under test.

Guava has a @VisibleForTesting annotation, but it's only for documentation purposes.


If you use FindBugs, I've built a plugin that can actually verify for you that @VisibleForTesting methods are not being used outside test classes.
I have a situation where my classes CAN NOT all be in the same package as my test class. What I did was instead of "make the private method protected or package-private" like you suggested, I made it public and then used @VisibleForTesting. The docs state this annotation will automatically set the visibility to private by default for production builds, so I'm not sure using anything less than public is necessary. This I believe is more flexible and allows you to put all your units tests in one package if you want.
@mpellegr what tool changes the visibility of your fields based on that annotation ? I checked the Guava docs and found nothing about that, not that Guava would be in a position to change that even if it wanted to, I think.
Android also have its own @VisibleForTesting annotation so that linter will warn you if you try to use the function outside of test.
C
Cygnusx1

If your test coverage is good on all the public method inside the tested class, the privates methods called by the public one will be automatically tested since you will assert all the possible case.

The JUnit Doc says:

Testing private methods may be an indication that those methods should be moved into another class to promote reusability. But if you must... If you are using JDK 1.3 or higher, you can use reflection to subvert the access control mechanism with the aid of the PrivilegedAccessor. For details on how to use it, read this article.


IMO it's very important to test at the inner-most point, so the small private method that some other public method calls. You shouldn't write a test for some "unrelated" outer public method, and have to set everything up perfectly so your inner/private method is hit just right. Instead, test the inner/private method on its own to verify its functionality regardless of from whence it's called.
I think this answer misses the point of the question: private methods are not made visible for testing to test them, but to access them. Probably to assert a certain state or to initialize with a mock object that is otherwise hard to inject.
N
Nathan Hughes

Consider using interfaces to expose the API methods, using factories or DI to publish the objects so the consumers know them only by the interface. The interface describes the published API. That way you can make whatever you want public on the implementation objects and the consumers of them see only those methods exposed through the interface.


s
simpatico

dp4j has what you need. Essentially all you have to do is add dp4j to your classpath and whenever a method annotated with @Test (JUnit's annotation) calls a method that's private it will work (dp4j will inject the required reflection at compile-time). You may also use dp4j's @TestPrivates annotation to be more explicit.

If you insist on also annotating your private methods you may use Google's @VisibleForTesting annotation.


My Kaspersky immediately triggered when visiting this page. The offending script is xpjxpj8.js I neither have the expertise nor the time to confirm or deny that. Please proceed with caution.
Use this link: github.com/gk2go/dp4j
S
Stan Kurilin

Or you can extract this method to some strategy object. In this case you can easily test extracted class and don't make method public or some magic with reflection/bytecode.


A
Atreys

An article on Testing Private Methods lays out some approaches to testing private code. using reflection puts extra burden on the programmer to remember if refactoring is done, the strings aren't automatically changed, but I think it's the cleanest approach.


f
facundofarias

Okay, so here we have two things that are being mixed. First thing, is when you need to mark something to be used only on test, which I agree with @JB Nizet, using the guava annotation would be good.

A different thing, is to test private methods. Why should you test private methods from the outside? I mean.. You should be able to test the object by their public methods, and at the end that its behavior. At least, that we are doing and trying to teach to junior developers, that always try to test private methods (as a good practice).


A robust ideology I find is using referentially transparent methods where possible for logic, and wrapper impure methods around them to pass parameters / do something with result. As far as integration testing goes, true, one should test public wrapper methods. However, with unit test it makes much more sense to test those pure private logic methods. Mocking is a solution, but it's IMO uglier solution that just testing logic directly
C
Community

I am not aware of any such annotation, however the following may be of value: unit testing private methods
or the following: JMockit


J
Joeri Hendrickx

You can't do this, since then how could you even compile your tests? The compiler won't take the annotation into account.

There are two general approaches to this

The first is to use reflection to access the methods anyway

The second is to use package-private instead of private, then have your tests in the same package (but in a different module). They will essentially be private to other code, but your tests will still be able to access them.

Of course, if you do black-box testing, you shouldn't be accessing the private members anyway.


could you give an example?
S
Snicolas

We recently released a library that helps a lot to access private fields, methods and inner classes through reflection : BoundBox

For a class like

public class Outer {
    private static class Inner {
        private int foo() {return 2;}
    }
}

It provides a syntax like :

Outer outer = new Outer();
Object inner = BoundBoxOfOuter.boundBox_new_Inner();
new BoundBoxOfOuter.BoundBoxOfInner(inner).foo();

The only thing you have to do to create the BoundBox class is to write @BoundBox(boundClass=Outer.class) and the BoundBoxOfOuter class will be instantly generated.


C
Community

As much as I know there is no annotation like this. The best way is to use reflection as some of the others suggested. Look at this post:
How do I test a class that has private methods, fields or inner classes?

You should only watch out on testing the exception outcome of the method. For example: if u expect an IllegalArgumentException, but instead you'll get "null" (Class:java.lang.reflect.InvocationTargetException).
A colegue of mine proposed using the powermock framework for these situations, but I haven't tested it yet, so no idea what exactly it can do. Although I have used the Mockito framework that it is based upon and thats a good framework too (but I think doesn't solve the private method exception issue).

It's a great idea though having the @PublicForTests annotation.

Cheers!


R
Roger Keays

I just put the test in the class itself by making it an inner class: https://rogerkeays.com/how-to-unit-test-private-methods