首页 文章

Mockito - 存根方法时的NullpointerException

提问于
浏览
27

所以我开始为Java-Spring项目编写测试 .

我使用的是JUnit和Mockito . 据说,当我使用when()... thenReturn()选项时,我可以模拟服务,而不需要模拟它们 . 所以我想做的是,设置:

when(classIwantToTest.object.get().methodWhichReturnsAList(input))thenReturn(ListcreatedInsideTheTestClass)

但无论我在哪个when子句中,我总是得到一个NullpointerException,当然这是有意义的,因为输入是null .

当我尝试从对象模拟另一个方法时:

when(object.method()).thenReturn(true)

在那里我也得到一个Nullpointer,因为该方法需要一个未设置的变量 .

但我想使用when().. thenReturn()来绕过创建这个变量等等 . 我只是想确保,如果任何类调用此方法,那么无论如何,只返回true或上面的列表 .

这是我身边的一个基本误解,还是有其他错误?

Code:

public class classIWantToTest implements classIWantToTestFacade{
        @Autowired
        private SomeService myService;

        @Override
        public Optional<OutputData> getInformations(final InputData inputData) {
            final Optional<OutputData> data = myService.getListWithData(inputData);
            if (data.isPresent()) {
                final List<ItemData> allData = data.get().getItemDatas();
                    //do something with the data and allData
                return data;
            }

            return Optional.absent();
        }   
}

这是我的测试类:

public class Test {

    private InputData inputdata;

    private ClassUnderTest classUnderTest;

    final List<ItemData> allData = new ArrayList<ItemData>();

    @Mock
    private DeliveryItemData item1;

    @Mock
    private DeliveryItemData item2;



    @Mock
    private SomeService myService;


    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
        myService = mock(myService.class); 
        classUnderTest.setService(myService);
        item1 = mock(DeliveryItemData.class);
        item2 = mock(DeliveryItemData.class);

    }


    @Test
    public void test_sort() {
        createData();
        when(myService.getListWithData(inputdata).get().getItemDatas());

        when(item1.hasSomething()).thenReturn(true);
        when(item2.hasSomething()).thenReturn(false);

    }

    public void createData() {
        item1.setSomeValue("val");
        item2.setSomeOtherValue("test");

        item2.setSomeValue("val");
        item2.setSomeOtherValue("value");

        allData.add(item1);
        allData.add(item2);


}

10 回答

  • 3

    我有这个问题,我的问题是我用any()而不是anyInt()调用我的方法 . 所以我有:

    doAnswer(...).with(myMockObject).thisFuncTakesAnInt(any())
    

    我不得不把它改成:

    doAnswer(...).with(myMockObject).thisFuncTakesAnInt(anyInt())
    

    我不知道为什么会产生NullPointerException . 也许这将有助于下一个可怜的灵魂 .

  • 32

    我有同样的问题,我的问题只是我没有使用@RunWith正确地注释该类 . 在您的示例中,请确保您具有:

    @RunWith(MockitoJUnitRunner.class)
    public class Test {
    ...
    

    一旦我这样做,NullPointerExceptions就消失了 .

  • 0

    您尚未存根的方法的默认返回值是 false 用于布尔方法,空集合或映射用于返回集合或映射的方法,否则为 null .

    这也适用于 when(...) 中的方法调用 . 在你的例子中 when(myService.getListWithData(inputData).get()) 将导致NullPointerException,因为 myService.getListWithData(inputData)null - 它之前没有被存根 .

    一个选项是为所有中间返回值创建模拟,并在使用前将它们存根 . 例如:

    ListWithData listWithData = mock(ListWithData.class);
    when(listWithData.get()).thenReturn(item1);
    when(myService.getListWithData()).thenReturn(listWithData);
    

    或者,您可以在创建模拟时指定不同的默认答案,以使方法返回新的模拟而不是null: RETURNS_DEEP_STUBS

    SomeService myService = mock(SomeService.class, Mockito.RETURNS_DEEP_STUBS);
    when(myService.getListWithData().get()).thenReturn(item1);
    

    您应该阅读Mockito.RETURNS_DEEP_STUBS的Javadoc,它更详细地解释了这一点,并且还有一些关于其用法的警告 .

    我希望这有帮助 . 请注意,您的示例代码似乎有更多问题,例如缺少断言或验证语句以及在模拟上调用setter(这没有任何影响) .

  • 0

    Corner case:
    如果您正在使用 Scala 并尝试在 value class 上创建一个 any 匹配器,那么您将获得无用的NPE .

    所以 case class ValueClass(value: Int) extends AnyVal ,你要做的是 ValueClass(anyInt) 而不是 any[ValueClass]

    when(mock.someMethod(ValueClass(anyInt))).thenAnswer {
       ...
       val v  = ValueClass(invocation.getArguments()(0).asInstanceOf[Int])
       ...
    }
    

    另一个SO question更具体地说明了这一点,但是你知道这个问题与 Value 类有关 .

  • 3

    对我而言,我获得NPE的原因是我在模拟基元时使用了 Mockito.any() . 我发现通过切换到使用mockito的正确变体来摆脱错误 .

    例如,要模拟一个以原始 long 作为参数而不是使用 any() 的函数,您应该更具体,并将其替换为 any(Long.class)Mockito.anyLong() .

    希望能帮助别人 .

    干杯

  • 1
    @RunWith(MockitoJUnitRunner.class) //(OR) PowerMockRunner.class
    
    @PrepareForTest({UpdateUtil.class,Log.class,SharedPreferences.class,SharedPreferences.Editor.class})
    public class InstallationTest extends TestCase{
    
    @Mock
    Context mockContext;
    @Mock
    SharedPreferences mSharedPreferences;
    @Mock
    SharedPreferences.Editor mSharedPreferenceEdtor;
    
    @Before
    public void setUp() throws Exception
    {
    //        mockContext = Mockito.mock(Context.class);
    //        mSharedPreferences = Mockito.mock(SharedPreferences.class);
    //        mSharedPreferenceEdtor = Mockito.mock(SharedPreferences.Editor.class);
        when(mockContext.getSharedPreferences(Mockito.anyString(),Mockito.anyInt())).thenReturn(mSharedPreferences);
        when(mSharedPreferences.edit()).thenReturn(mSharedPreferenceEdtor);
        when(mSharedPreferenceEdtor.remove(Mockito.anyString())).thenReturn(mSharedPreferenceEdtor);
        when(mSharedPreferenceEdtor.putString(Mockito.anyString(),Mockito.anyString())).thenReturn(mSharedPreferenceEdtor);
    }
    
    @Test
    public void deletePreferencesTest() throws Exception {
    
     }
    }
    

    如果您使用 @Mock 注释 Context mockContext; ,则不需要{ mockContext = Mockito.mock(Context.class); }以上所有注释代码

    @Mock 
     Context mockContext;
    

    但是如果你只使用 @RunWith(MockitoJUnitRunner.class) 它会起作用 . 根据Mockito,您可以使用@Mock或Mockito.mock(Context.class)创建模拟对象; ,

    因为使用了@RunWith(PowerMockRunner.class),我得到了NullpointerException,而不是我改为@RunWith(MockitoJUnitRunner.class)它工作正常

  • 1

    对于未来的读者来说,使用模拟时NPE的另一个原因是忘记初始化模拟,如下所示:

    @Mock
    SomeMock someMock;
    
    @InjectMocks
    SomeService someService;
    
    @Before
    public void setup(){
        MockitoAnnotations.initMocks(this); //without this you will get NPE
    }
    
    @Test
    public void someTest(){
        Mockito.when(someMock.someMethod()).thenReturn("some result");
       // ...
    }
    
  • 55

    面对同样的问题,解决方案对我有用:

    我使用@InjectMocks来模拟服务实现,而不是模拟服务接口:

    @InjectMocks
    private exampleServiceImpl exampleServiceMock;
    

    代替 :

    @Mock
    private exampleService exampleServiceMock;
    
  • 28

    Ed Webb的answer在我的案例中有所帮助 . 相反,您也可以尝试添加

    @Rule public Mocks mocks = new Mocks(this);
    

    如果你 @RunWith(JUnit4.class) .

  • 0

    在我的情况下,我错过了先添加

    PowerMockito.spy(ClassWhichNeedToBeStaticMocked.class);
    

    所以这对看到这样的错误的人有帮助

    java.lang.NullPointerException
        at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.addAnswersForStubbing(PowerMockitoStubberImpl.java:67)
        at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:42)
        at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:112)
    

相关问题