How to Mock a Private Field

Mockito: Mock private field initialization

I already found the solution to this problem which I forgot to post here.

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Test.class })
public class SampleTest {

@Mock
Person person;

@Test
public void testPrintName() throws Exception {
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Test test= new Test();
test.testMethod();
}
}

Key points to this solution are:

  1. Running my test cases with PowerMockRunner: @RunWith(PowerMockRunner.class)

  2. Instruct Powermock to prepare Test.class for manipulation of private fields: @PrepareForTest({ Test.class })

  3. And finally mock the constructor for Person class:

    PowerMockito.mockStatic(Person.class);
    PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);

How do I mock a private field using PowerMockito?

See Whitebox.setInternalState(...).

For example - given class A which needs to be tested:

public class A {
private B b;

public A() {
b = new B();
}

public void doSomething() {
b.doSomething();
}

}

which has a private instance of B:

public class B {

public void doSomething() {
// some long running, resource intensive process...
System.out.println("Real B.doSomething() was invoked.");
}

}

then Whitebox can be used to set the private state of A so it can be tested:

import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.powermock.reflect.Whitebox;

@RunWith(MockitoJUnitRunner.class)
public class ATest {

@Mock
private B b;

private A a;

@Before
public void prepareTest() {
doNothing().when(b).doSomething();

a = new A();
Whitebox.setInternalState(a, B.class, b);
}

@Test
public void doSomething() {
a.doSomething();
verify(b).doSomething();
}

}

How do I mock a private field?

I'm almost citing techniques from "Working Effectively with Legacy Code":

  1. Sub-class your class in a unit test and supersede your private variable with a mock object in it (by adding a public setter or in the constructor). You probably have to make the variable protected.
  2. Make a protected getter for this private variable, and override it in testing subclass to return a mock object instead of the actual private variable.
  3. Create a protected factory method for creating ISnapshot object, and override it in testing subclass to return an instance of a mock object instead of the real one. This way the constructor will get the right value from the start.
  4. Parametrize constructor to take an instance of ISnapshot.

Mocking a Private Variable that is Assumed to Exist

After a lot more hunting around and looking at all the options Mockito/Powermock had to offer, I found the solution (which I will share in case others run into this same issue).

When you have private member variables that are never initialized (and just assumed created in other places), you can use the @InjectMocks annotation to "inject" Mocks you want into your class you are testing.

  1. Add a variable in your test class for the class you are testing, and give it the annotation @InjectMocks (org.Mockito.InjectMocks).
  2. Use @Mock annotations to setup the mocks you want to inject. Use the @Mock (name = "privateVariableNameHere") name property to map the Mock object to the private variable inside your class you are testing.
  3. In either a setup function or before you call your class, initialize the mocks. The easiest way I have found is to use a "setup" method with the @Before annotation. Then inside there call MockitoAnnotations.initMocks(this); to quickly initialize anything with the @Mock annotation.
  4. Define your Mock functionality in your test method (before calling the method you are testing).
  5. Using the @InjectMock object, call your method you are testing... the mocks SHOULD be hooked in and working as defined in the earlier steps.

So for the example class I use above, the code to test/mock would have Connection returned as a mock which you can do whatever with. Based on the example above in my question, this is what the code would look like:

@RunWith(PowerMockRunner.class)
@PrepareForTest({/* Static Classes I am Mocking */})
public class ExampleTest {
@Mock (name = "queueFactory") //same name as private var.
QueueConnectionFactory queueFactoryMock;
@Mock
Connection connectionMock; //the object we want returned
@InjectMocks
Example exampleTester; //the class to test

@Before
public void setup(){
MockitoAnnotations.initMocks(this); // initialize all the @Mock objects
// Setup other Static Mocks
}

@Test
public void testTestMe(){
//Mock your objects like other "normally" mocked objects
PowerMockito.when(queueFactoryMock.createConnection()).thenReturn(connectionMock);
//...Mock ConnectionMock functionality...
exampleTester.testMe();
}
}

Initialize private field in mocked class

In your case you could use a Spy instead of a mock.

This would trigger the constructor and assign an empty list to the private variable thus escaping the NPE:

private UsersLoader loader;

@Before
public void setUp() {
loader = spy(new UsersLoader());
}

Now you do not need make this call:

  doCallRealMethod().when(loader).loadExternalUsers(anyBoolean());

Just keep in mind that you need to mock any methods that do not need to be triggered as implemented.

Mockito not mocking private variables using @Spy

myObject = new MyObject(); - this is the root of the problem. Mockito instantiates the mock for you, but you're replacing the mock with your own myObject instance, what leads to NPE (obviously, the listOfStrings is null in your newly instantiated object).

That how it should work:

@RunWith(MockitoJUnitRunner.class)
public class MyObjectTest {

// Don't do this with List!
// Type 'List' is an interface and it cannot be spied on.
@Spy
private ArrayList<String> listOfStrings;
@InjectMocks
private MyObject myObject;

@Before
public void before() {
listOfStrings.addAll(List.of("test", "test2"));
}

@Test
public void testCallListOfStrings() {
Mockito.doReturn(new ArrayList().stream()).when(listOfStrings).stream();
myObject.callListOfStrings();
}
}

Output:

test
test2

Mocking a private field

You can't mock anything that's private, static, or essentially - non overridable (this comes as a free mocking libraries limitation).

What you usually do in such situations (when it appears that private member has to be tested), is extracting your private member to a separate class and inject it to tested class as a dependency.

In your case, you actually need to extract code that creates _sMediaPlanObjective, which is this line:

this._sMediaPlanObjective =
(MPWrapper.Instance).getMediaPlanObjective(sMediaPlanId);

Object that provides getMediaPlanObjective method should be injected to your tested class. If you do so, you can simply mock that object and tell it to return mocked version of _sMediaPlanObjective.



Related Topics



Leave a reply



Submit