Changing private methods to protected for testing - java

Is it a good idea to change the private methods to protected for JUNIT testing.?

It's sometimes useful, yes.
If the class is extendable, make sure to make the method final.
Also, document the fact that the method is not supposed to be called by subclasses or external classes of the same package.
I use the Guava #VisibleForTesting annotation to make it clear that the method should in fact be private.

No in general not. The idea of unit testing is to test ... units. Or in other words implementations of interface methods.
If you want to test a method which you can't "see" this could be a code smell. Maybe you haven't separated your business logic enough from the UI code or something.
So the best idea would be to rethink your architecture. But if the alternative would be to not test your code it is a good idea to make those methods protected.

You can make the methods package local instead.
You can call private method using reflection, or you can decide that private methods shouldn't be tested directly, only indirectly.

Although you should prefer to refactor as #user714965 suggested, PowerMock's MockPrivate can do the mocking without opening the visibility of your private methods.
Writing your tests first usually leads to a design where you don't need to mock private methods.

Related

java - testing - lambda with inner private method?

I have some problems with unit testing the following method.
public List<GetSupplyChainResponse> getSupplyChains(){
List<GetSupplyChainsResponse> response = new ArrayList<>();
supplyChainRepository.findSupplyChainsWithCompound().forEach(result
-> response.add(getGetSupplyChainSimpleResponse(result)));
return response;
}
getGetSupplyChainSimpleResponse() is a private method of the same class as getSupplyChains()
Is there any possibility to define return values therefore or do you have any other ideas how I could test the method getSupplyChains()?
You might be overthinking this. The fact that the method that you want to test (getSupplyChains) uses a lambda that calls a private method is irrelevant: they are just implementation details.
What you unit test is the part of your class that you as a client interact with, i.e. its interface. You typically call a public method with some arguments (in this case there are none), you get some return value and that is what you verify in your unit test. If your public method makes use of some private method, it will be tested also.
The problem here is that the response that you get from getSupplyChains obviously depends on what supplyChainRepository.findSupplyChainsWithCompound() returns. What you do in this case is mock that dependency (supplyChainRepository) out: you create a mock instance of SupplyChainRepository, you tell it how to behave, and you pass it to this class, for example via the constructor.
You can either write the mock yourself, or you can rely on a mocking framework to do the heavy lifting like Mockito.
I definitely recommend against unit testing private methods (it leads to brittle tests), or increasing the visibility of those methods (a.k.a. sacrificing your design for the sake of testing).
It is a commonly discussed problem, some prefer using reflection as Janos Binder recommended (How to call a private method from outside a java class), some can live with the fact the the visibility of the methods which we need to mock is increased for the sake of testability.
The problem is quite well discussed here: How do I test a class that has private methods, fields or inner classes?. You can see from the answers and wide discussions that the topic is quite complicated and developers split into factions and use different solutions.
I'd recommend you to remove the private access modifier and to make the method package private (use the default visibility). The common custom is to have the test classes in the same package (but not in the same folder!) as the tested classes.
This will allow you to:
Test the getGetSupplyChainSimpleResponse() method itself, which you should do in any case somehow.
Mock its behaviour for the purpose of testing getSupplyChains(). This you will achieve e.g. by using Mockito framework and its #Spy functionality.
For those who argue that this is "sacrificing your design for the sake of testing", I would answer that if you have private methods which need to be mocked and/or tested, then the design is not ideal anyway so changing the visibility to package private does not mean too big deterioration. The clear solution (from the object oriented design's point of view) is to delegate the behavior of such private methods to separate classes. However, sometimes in the real life it is just overkill.

what is the advantage of using #VisibleForTesting? [duplicate]

This question already has answers here:
How do I test a class that has private methods, fields or inner classes?
(58 answers)
Closed 5 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...
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 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.
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.
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.
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.
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.
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).
I am not aware of any such annotation, however the following may be of value: unit testing private methods
or the following: JMockit
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.
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.
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!
I just put the test in the class itself by making it an inner class:
https://rogerkeays.com/how-to-unit-test-private-methods

Providing package-private methods for testing?

I'm writing the following class:
public class MyClass{
private final MyAnotherClass[][] anotherClasses;
public MyClass(//args){
//initialization
}
//...
}
I'd like to test this class, but wouldn't like to provide any methods exposing its iternal state (anotherClasses array in this case). The test is going to test the state of the array, so we have to access it.
Is it considering as a good practice to provide a method, say
MyAnotherClass[][] getAnotherClasses(){
retun anotherClasses;
}
with package-private visibility to use it only for testing? I'm kind of not sure about that. We introduce some annoying coupling between the class and its test...
Maybe there's a better approach to deal with that?
As already mentioned, it is not good practice to test non-public methods. But sometimes if it is really needed for me, I'm using #VisibleForTesting annotation from guava.

How to practice TDD in Android if most of my activities' methods are private?

I read a little about TDD and testing in general and decided I wanna give it all a try.
After finding out how to run tests in Android Studio, I realized most of my methods are private (thus making it wrong to test them directly, as per this link: How do I test a class that has private methods, fields or inner classes? ).
What gives? Is this a design smell? Should I refactor somehow?
Given that I should only be able to write code after a failing test, how would I go about classes that have very few public methods, such as most of my activities?
Did I miss something?
Please have in mind that I'm new to this.
Most people I talk about this agree that you should only test your API methods. If you assume that the purpose of a class private methods is to support the public methods, then the fact that your public methods are working correctly kind of implies that your private methods are also working correctly.
When I have a private method that is somewhat complex and that I really think should be unit tested, I simply make it package-private (no modifier) so it's not exposed in the class API but can still be accessed by the test class.
Addressing your questions specifically:
What gives? Is this a design smell? Should I refactor somehow?
It doesn't sound like a design smell. On the contrary, having more private methods than public methods probably means that your class code is modular and follows the encapsulation principle.
Given that I should only be able to write code after a failing test, how would I go about classes that have very few public methods, such as most of my activities? Did I miss something?
Assuming you are creating a new class from scratch, I would do something like this:
Design the class API methods
For each API method:
Write the unit tests for the method
Write the code until the tests pass
Refactor the method, extracting private methods as needed
If you do it this way, you can be sure that your private methods are covered, since you tested their functionality through the public method.
Also, a code coverage tool such as JaCoCo or Cobertura may be helpful in finding which parts of your code lack proper testing.

Is it OK to make a private method to non-private just because to make it easier for testing?

We are maintaining some legacy code and we are adding tests for them.
In order to write tests easier, we will extract some code into methods(which will be private be default), then writing tests for them. Since they are private, it's hard to invoke them, so what we did is: remove the "private" keyword, make it "non-private", then write tests.
But it makes some methods non-private which are supposed to be "private". Is it OK to do this, or is there any better solutions?
Update:
I think I'd better to provide some more background:
The code is very complicated and not good, and has nearly no tests. Writing tests for public methods are not possible since they have lots of hard-coded external dependencies and which are not be mocked.
We need to fix some bugs, but without tests we can't do it. So we first extract some related code to some methods, add tests for them, then fix bugs.
Since the extracted methods are private by default, we have to make them non-private, otherwise we have to invoke them by reflection:
Method method = Somecls.class.getMethod("a-private-method");
method.setAccessible(true);
method.invoke(...)
But if we make it non-private, we can just:
Somecls cls = new Somecls();
cls.nonPrivateMethod(...)
If the method is private, then it will be invoked by a public method in that class. Ideally, the test cases should pass through the public method to reach all the possible flows in that private method.
I think this is something pretty personal. In most of the scenarios I don't see any harm on changing the scope of a method from private to protected/package private if this helps to write better unit tests. It not only allows to invoke the method from your test - which you might do either way using reflection -, but also to override/mock that method when testing another one invoking this.
However, if you don't want to do it you still can use tools like Spring's ReflectionTestUtils to make the invocation of private methods less painful.
Yes this is perfectly acceptable for creating unit tests for the private methods. By leaving out the private keyword you create what is a known as a package-private method which can only be accesed by other classes that are in the same package. This lets you create unit test classes in the same package that can call your methods but also provides encapsulation so clients that use your class can't call those methods.

Categories