简体   繁体   中英

Mockito not mocking private variables using @Spy

I am attempting to mock a private instance variable listOfStrings using Mockito's @Spy annotation.

@RunWith(MockitoJUnitRunner.class)
public class MyObjectTest {

  @InjectMocks
  MyObject myObject;
  @Spy
  List<String> listOfStrings;

  @Before
  public void before() {
    myObject = new MyObject();
  }

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

and

public class MyObject {

  private List<String> listOfStrings;

  public void callListOfStrings() {
    listOfStrings.stream().forEach(System.out::println);
  }
}

It does not appear to be mocking the instance variable, throwing a NullPointerException , obviously.

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

You forgot to call Mockito.initMocks(this) inside before() . Also, you don't need to initialize myObject because mockito will initialize it for you.

I would also like to note that your test really isn't doing anything, because you're not asserting or verifying anything, you're just calling the MyObject.call ListOfString() method.

Did you try With Java Reflection. I think you need to avoid the null pointer exception.

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.runners.MockitoJUnitRunner;

import java.lang.reflect.Field;
import java.util.ArrayList;

@RunWith(MockitoJUnitRunner.class)
public class MyObjectTest {

    @InjectMocks
    MyObject myObject;

    @Before
    public void before() {
        myObject = new MyObject();
    }

    @Test
    public void testCallListOfStrings() throws NoSuchFieldException, IllegalAccessException {

        Field list = myObject.getClass().getDeclaredField("listOfStrings");
        list.setAccessible(true); // Suppress Java language access checking
        list.set(myObject,new ArrayList<>());
        myObject.callListOfStrings();
    }
}

You can assign a value for the private variable

listOfStrings

That will avoid the null pointer exception.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM