Accessing non public Method of Object in JUNIT - junit

Hi I am new to unit testing. Is it possible to access methods that are private?
A very simple example
ObjectA
----------
File file;
private void setupFile (){
//do something
file = "C:\file.dat"
}
In TestCase
File sth = ObjectA.setupFile();
assertNotNull(sth);
I am unable to test whether the file variable is null in method ObjectA.setup()
as I cannot run ObjectA.setupFile()
I am not sure about whether doing like this make sense in terms of unit testing.
So is that a better practice to write every method returning sth and set them public for easier unit testing?
Thanks in advance

In general, you should avoid changing the access of a method/field to enable testing. If you do this then you risk developers using the method directly.
However, if you do need to, then making it protected as Deco says is a good way, so it's accessible from the JUnit tests. If you do this, make sure that it is well documented that this is an method for internal use.
A better way is to test the behaviour of the public methods; you shouldn't care about internal implementation details of a class, so you should only be testing public methods. It's hard to tell from your code, but presumably, the setupFile() has effects later on other methods, so you can test those effects, not the fact that file is not null.
External dependencies (such as dependencies on file system, environment variables) can be worked around in your tests, or injected directly into the class. For the general principle, see my answer to How to test code dependent on environment variables using JUnit?

If it is not absolutely necessary to have the method as private, you can have it as package private (i.e. default access) so that you can call it directly in a JUnit test.
Package private methods can only be used in the package that they are declared, and do not become part of the API of the class. You declare a method package private by putting no modifier on it's declaration.
Here's an example to demonstrate:
public class MyClass() {
int foo;
public MyClass() {
this.foo = 0;
}
void notSoComplexCalculationMethod(int a) {
foo = a * 2;
}
//Other methods here . . .
}
public class MyClassTest extends TestCase {
private MyClass myClass;
protected void setUp() {
super.setUp();
myClass = new MyClass();
}
public void testNotSoComplexCalculationMethod() {
int a = 2;
assertEquals(4, myClass.notSoComplexCalculationMethod(a));
//Unit test passes, yay! Now you've tested a package private method.
}
}

Related

Unable to Mock functions inside static method Powermock

I am writing unit test for the below code using junit and mockito
public class Abc implements Runnable
{
private static ServerSocket server;
private static int port;
public Abc(int cPort)
{
port = cPort;
}
public void run()
{
init();
}
public static void init()
{
try {
server = new ServerSocket(port);
...something...
client.close();
}
}
catch(IOException e)
{
System.out.println("Exception inside init()...");
e.printStackTrace();
}
}
};
Unit test I have written
#RunWith(PowerMockRunner.class)
#PrepareForTest({ServerSocket.class})
public class abcTest {
#Mock (name = "server") //same name as private var.
ServerSocket mockServer;
#InjectMocks
Abc abc;
#Test
public void testInit() throws Exception {
int port = 1880;
Socket mockClient = Mockito.mock(Socket.class);
PowerMockito.whenNew(ServerSocket.class).
withArguments(anyInt()).thenReturn(mockServer);
abc = new Abc(port);
Abc.init();
PowerMockito.verifyNew(ServerSocket.class).withArguments(port);
}
};
But the call always go to original function definition. I am using junit 4.11 with mockito 2.28.2 and powermockito 2.0.2. I'm using java after a long time. Now its feel like kind of new. Please correct me if anything wrong in the code also.
You will need to change your PrepareForTest annotation
to #PrepareForTest({Abc.class}).
From the PowerMockito docu:
This annotation tells PowerMock to prepare certain classes for testing. Classes needed to be defined using this annotation are typically those that needs to be byte-code manipulated
In this case that refers to the class which creates the new instance of ServerSocket. ServerSocket itself is a non-final public class that does not require special handling from PowerMockito (instead Mockito can deal with this class on its own).
You could also change your test to do the following:
#Test
public void testInit() throws Exception {
int port = 1880;
ServerSocket mockServer = Mockito.mock(ServerSocket.class);
PowerMockito.whenNew(ServerSocket.class)
.withArguments(Mockito.anyInt()).thenReturn(mockServer);
Abc.port = port;
Abc.init();
PowerMockito.verifyNew(ServerSocket.class).withArguments(port);
}
(This first point is unrelated to whether the test fails or succeeds)
I do not know why you mix object's and static method behaviour together, but I think you should change that.In the test instead of creatic an ABC object, just could just set the static port variable directly.
Or alternatively change the whole ABC class into an object.
#InjectMocks failed for me as there is no default constructor
(Actually I got an error message in the console when trying to execute your code)
Additonaly you create a new instance of ABC in your test, which would have overwritten the things done by the annotations. Also as server is created during the init call, there is no need to inject a mock for it.
powermockito 2.0.2 actually depends on junit 4.12, so I am not sure what effects downgrading to an older version might have.
Socket mockClient seemed somewhat unrelated to the code your posted, so I removed it from my example in the answer, however as you use a client (I assume that is your Socket) in your code your probably need to do some mocking for that as well and provide the mock to the method accordingly.

Mocking New Object Call In Superclass

I am having a hard time getting this one piece of mocking figured out for my unit tests. The classes in question are all part of legacy code that I don't really have the option of changing right now (I am hoping to be able to do some refactoring in the future, but need tests now).
Here are the two classes that I am dealing with, and the specific part I am having trouble with. Class A declares an object using new and then class B uses the object. I am trying to mock the object but I keep getting the real version of it instead of the mocked version.
public class B extends A(){
...
int x = problemObj.doMethod();
}
public class A(){
...
ProblemObj problemObj = new ProblemObj();
}
Here is my test class.
#RunWith(PowerMockRunner.class)
#PrepareForTest({A.class, B.class})
public class ATest(){
private ProblemObj problemObjMock;
#Before
public void setUp(){
problemObj = PowerMockito.Mock(ProblemObj.class);
}
#Test
public void test(){
PowerMockito.whenNew(ProblemObj.class).withNoArguments().thenReturn(problemObj);
...//rest of test below here
}
}
I have done other whenNew mocking in tests and set it up the same way as this. But for some reason this object being in the superclass is really throwing me off.
Versions used are Junit:4.11, Mockito:1.9.5, Powermock: 1.6.6

Ignoring invoking internal static call

public static ResponseBean call(Bean bean) throws Exception {
// statements...
IgnoreCall.ignoreMethodCall(bean);
// statements...
// return
}
With the code snippet above, is it possible to test the method ignoring invocation of IgnoreCall.ignoreMethod(Bean) without needing to place the entire statement under a boolean condition?
Here's the unit test code snippet:
#RunWith(PowerMockRunner.class)
#PrepareTest
public ClassHelperTest {
#Test
public void testCall() throws Excpetion {
// stubbing...
ResponseBean responseBean = ClassHelper.call(bean);
// verify/ies
// assert/s
}
}
Notes:
Refactoring ClassHelper.call(Bean) should be avoided. Even with a bad OO design, refactoring is costly.
Method signature is locked unless another pattern is applicable for replacement.
Tried using Mockito.when and PowerMockito.when on the target static method, stubbing didn't work on run-time debug.
As your comments indicate that changing your production code is not possible, you "simply" have to dive into the static-mocking aspects of PowerMock; as outlined here for example.
Basically you need to enable IgnoreCall for static mocking; and then you make calls to ignoreMethodCall() a no-op.
But as you keep asking: the core problem with your question is the fact that you want to mock out a static method that is void. I have a complete example below, but before that some explanations.
The point is: you call a method for two reasons:
It has a side effect
It returns a value, and maybe, causes a side effect, too
A void method can only be called for side effects. And the thing is: when you do static mocking, then that works on class level.
Meaning: you instruct PowerMock to "prevent" any of the static methods of some class from execution; you simply "erase" the side effects of all those static methods! So, by telling PowerMock to do those static mocks, all void methods are already "gone".
But as said, you might also call methods for their return value. And then is when the when() method of Mockito kicks in. You use that method to say: when that value-returning method is invoked, then do this or that.
Long story short; here is a [mcve] using the elements you asked for:
package ghostcat.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
class IgnoreCall {
public static void ignoreMethodCall(Object o) {
System.out.println("SHOULD NOT SHOW UP: " + o);
}
}
class CuT {
public static Object call(Object bean) {
System.out.println("statement1");
IgnoreCall.ignoreMethodCall(bean);
System.out.println("statement2");
return "whatever";
}
}
#RunWith(PowerMockRunner.class)
#PrepareForTest(IgnoreCall.class)
public class PMTest {
#Test
public void test() {
PowerMockito.mockStatic(IgnoreCall.class);
CuT.call("yeha");
}
}
As in your example ... there is IgnoreCall; used within that a static method that I just called "call".
This prints:
statement1
statement2
When I go in and comment out
// PowerMockito.mockStatic(IgnoreCall.class);
It prints:
statement1
SHOULD NOT SHOW UP: yeha
statement2
So, a simple example that should tell you exactly what you need to do.
I worked with eclipse neon, IBM java8 JDK, and simply imported all the JARs from powermock-mockito-junit-1.6.6.zip into my test project.

Load a mocked class in Mockito

Is there an easy way, using Mockito, to load a mock class when another is requested OR to override the test ClassLoader?
Basically I have a class Foo that has a member "ClassA" in it. I want to replace to use "TestClassA" instead of "ClassA" during testing. I don't want to use dependency injection because it doesn't make any sense for actual operation. (It can never be anything other than ClassA)
Can I do this?
It can never be anything other than ClassA
...except that it is, in your test. Test code is real code, and though that doesn't mean it should sneak into your production application, it does mean that you need to write in the flexibility you need for all of its use cases, and that includes testing.
Mockito works via subclasses: A mockFoo created by mock(Foo.class) or #Mock Foo mockFoo is actually a proxy subclass Mockito created that overrides each of Foo's methods. As you can tell from that description, Mockito thus cannot change the behavior of every Foo object and especially cannot change the type of the object returned from new Foo().
You have two options, that I can see:
Accept a ClassA or InterfaceA instance in one of your constructors. If you put your tests in the same Java package as your class under test (even in a different source tree), you can even make the constructor package-private, or keep it private and create a static factory method like createForTest(ClassA).
Example:
public class ConsumerToTest {
private final ClassA classA;
/** For public use. */
public ConsumerToTest() {
this(new ClassA());
}
/** For use in tests. */
ConsumerToTest(ClassA class) {
this.classA = classA;
}
// ...
}
Use PowerMock, which has a Mockito integration known as PowerMockito. Though Mockito uses pure proxy subclasses and code generation, PowerMockito actually rewrites the bytecode of the system-under-test. This means that you can mock static methods and constructors that Mockito couldn't adjust on its own through polymorphism.
Personally, I very much prefer solution 1: The code is yours to control, and as long as you're clear that your test is a first-class consumer of your system-under-test, you're free to design it to be testable in the first place.
Doing it by constructor is what I prefer.
For example
public class Foo {
private ClassA classA;
public Foo(ClassA classA) {
this.classA = classA;
}
}
public class FooTest {
private Foo foo;
#before
public void setup() {
foo = new Foo(Mockito.mock(ClassA.class);
}
}
It's really simple to do this using Mockito.
public class Foo {
private ClassA classA;
}
Test will look like this:
#RunWith(MockitoJUnitRunner.class)
public class FooTest {
#Mock
private ClassA classA;
#InjectMocks
private Foo foo = new Foo();
//Test methods
}
That's it, you have mocked ClassA!

Mockito/PowerMock when....then not being invoked?

When I execute the following test, the original object is being returned and not the mock, so the real method getLevelCriteriaForLevel(level) is being executed (I observed that with the debugger). Why is that so? I'm pretty sure that this already worked yesterday and I didn't change anything there.
I already tried
#PrepareForTest({LevelCriteria.class, LevelGenerator.class})
or used the MockitoJunitRunner as I did before, but this doesn't help either.
Here is the code (generateConcreteLevel is a private method. The expected exception is only thrown when I pass this data from the mock. Otherwise it's not thrown. The test fails because the exception is not thrown, because the test doesn't use the mock object but the real object):
public class LevelGenerator
{
public void createLevel(int level)
{
generateConcreteLevel(levelCriteria.getLevelCriteriaForLevel(level));
}
private void generateConcreteLevel(LevelCriterion levelCriterion)
{
int entryGroupCount = levelCriterion.getEntryGroupCount();
int exitGroupCount = levelCriterion.getExitGroupCount();
int exitsWhileEntries = levelCriterion.getExitsWhileEntries();
int maxGroupSize = levelCriterion.getMaxGroupSize();
List<Question> questions = levelCriterion.getQuestions();
int speed = levelCriterion.getSpeed();
Range blueItemsCount = levelCriterion.getBlueItemsCount();
Range brownItemsCount = levelCriterion.getBrownItemsCount();
Range greenItemsCount = levelCriterion.getGreenItemsCount();
Range redItemsCount = levelCriterion.getRedItemsCount();
Range whiteItemsCount = levelCriterion.getWhiteItemsCount();
Range timespanBetweenGroups = levelCriterion.getTimespanBetweenGroups();
float fractionOfCarAmountToLeave = levelCriterion.getFractionOfCarAmountToLeave();
float fractionOfMinimumItemsAmountInCarParkToStartExits = levelCriterion.getFractionOfMinimumItemsAmountInCarParkToStartExits();
checkItemsFitInEntryGroups(entryGroupCount, maxGroupSize, blueItemsCount, brownItemsCount, greenItemsCount, redItemsCount, whiteItemsCount);
}
private void checkItemsFitInEntryGroups(int entryGroupCount, int maxGroupSize, Range... ranges)
{
int totalRangeCount = 0;
for (Range range : ranges)
{
totalRangeCount += range.getMaximum();
}
if (totalRangeCount > entryGroupCount * maxGroupSize)
{
throw new UnsupportedOperationException("Error in level criterion: Not enough entry groups for Items.");
}
}
}
and the test...
#RunWith(PowerMockRunner.class)
public class LevelGeneratorTest
{
#Mock
private LevelCriteria levelCriteriaMock;
#InjectMocks
private LevelGenerator levelGenerator;
#Test(expected=UnsupportedOperationException.class)
public void tooLessPositionsInEntryGroups()
{
LevelCriterion levelCriterion = new LevelCriterion.LevelCriterionBuilder()
.withBlueItemsCount(new Range(10, 10))
.withEntryGroupCount(3)
.withExitGroupCount(3)
.withMaxGroupSize(3)
.build();
when(levelCriteriaMock.getLevelCriteriaForLevel(anyInt())).thenReturn(levelCriterion);
levelGenerator.createLevel(0);
}
}
By the way: It's not an Eclipse problem, since a Maven build produces the same error.
I know that I can execute the private method with PowerMockito's Whitebox directly what I will do after refactoring, but the question is, why when...then is not working here.
Assuming your question is:
Why is the real method getLevelCriteriaForLevel(level) executed when I expect to have the mocked implementation?
I will try to demonstrate that everything works as expected fine if you reproduce it is a simple example (switch from your current workspace if you need to).
I do not have you LevelCriterion.LevelCriterionBuilder() so I simplified your test by just instantiating a LevelCriterion (empty object). This is not relevant because in my implementation I will always return an UnsupportedOperationException.
Here is how my LevelGenerator looks like. The UnsupportedOperationException is as simple as possible LevelGenerator#generateConcreteLevel(LevelCriterion).
If implemented like this:
public class LevelGenerator
{
LevelCriteria levelCriteria;
public void createLevel(int level)
{
generateConcreteLevel(levelCriteria.getLevelCriteriaForLevel(level));
}
private void generateConcreteLevel(LevelCriterion levelCriteriaForLevel)
{
throw new UnsupportedOperationException("xxx");
}
}
The test is green, using a #RunWith(MockitoJUnitRunner.class) on top of LevelGeneratorTest.
I did not used #PrepareForTest or PowerMockRunner. I have Mockito version 1.9.5.
I am sure that I have not used the real getLevelCriteriaForLevel(level) implementation, because it looks like this:
public class LevelCriteria {
public LevelCriterion getLevelCriteriaForLevel(int level) {
throw new IllegalStateException("Unexpected call");
}
}
If the real getLevelCriteriaForLevel(int) is excuted the test is red (because an IllegalStateException is thrown).
And the test is still green...
This works with pure-Mockito (version 1.9.5) approach.
You should remove all other mocking library (PowerMock, EasyMock...)
Are you sure that your static call of when(..).thenReturn(..) is the Mockito one?
Have you tried: Mockito.when(..).thenReturn(..)
I hope it helps.
PS: I have seen that you updated the question. It is a little bit unfair for me, but I am not going to update my answer once again.
I do not need the implementation detail of generateConcreteLevel(..) to illustrate that the when(..) call is working. Read my answer again. Try it in a separate workspace and you will see by yourself.
PS-2: I am using Eclipse too... Why should it be a problem? Eclipse is a great IDE.
I figured out what's wrong. Unfortunately you don't see the real problem in this question since I simplified the classes:
My real implementation of LevelGenerator got a parameterized constructor today. That's the reason why it worked yesterday but not anymore. Obviously there are mock injection problems with Mockito when not using a standard constructor.
Result: The when...then works as excpected.