Mockito - 存根方法时的NullpointerException

时间:2015-10-14 11:30:13

标签: java testing junit mockito stubbing

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

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

 s = new Student();
s.constructor // returns function Person(){} 

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

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

s.constructor which is s.prototype.constructor would show function Person(){}

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

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

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

代码:

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

这是我的测试课:

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

22 个答案:

答案 0 :(得分:78)

我遇到了这个问题,我的问题是我使用any()而不是anyInt()来调用我的方法。所以我有:

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

我必须将其更改为:

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

我不知道为什么会产生NullPointerException。也许这会帮助下一个可怜的灵魂。

答案 1 :(得分:39)

对于布尔方法,尚未存根的方法的默认返回值为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(这没有任何影响)。

答案 2 :(得分:32)

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

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

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

答案 3 :(得分:7)

请确保您初始化了模拟程序。

JUnit4使用@Before

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

JUnit5使用@BeforeEach

@BeforeEach
public void setup() {
    MockitoAnnotations.initMocks(this);
}

对于JUnit5检查,您还使用了正确的导入。

import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)

答案 4 :(得分:5)

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

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

希望能有所帮助。

答案 5 :(得分:3)

转角案例
如果您正在使用 Scala 并尝试在值类上创建any匹配器,那么您将获得无用的NPE。< / p>

所以给定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更具体地说明了这一点,但是当你不知道问题与价值等级有关时,你会错过它。

答案 6 :(得分:2)

对于将来的读者而言,使用模拟时导致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");
   // ...
}

答案 7 :(得分: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 {

 }
}

以上所有评论代码均不是必需的 {mockContext = Mockito.mock(Context.class);}, 如果您使用 @Mock 注释到Context mockContext;

@Mock 
Context mockContext; 

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

由于使用了@RunWith(PowerMockRunner.class),我得到了NullpointerException,而不是我改为@RunWith(MockitoJUnitRunner.class),它运行良好

答案 8 :(得分:1)

对于 JUnit 5,测试类必须注解:

@ExtendWith(MockitoExtension.class)

进口:

import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

这个添加解决了我的问题。

答案 9 :(得分:1)

您需要初始化MockitoAnnotations.initMocks(this)方法必须调用以初始化带注释的字段。

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }

有关更多详细信息,请参见Doc

答案 10 :(得分:0)

在我的情况下,这是因为错误的注释使用。我使用 junit 4 进行测试并在初始化时使用 @BeforeEach 而不是 @Before

将其更改为 @Before,它的作用就像魅力一样。

答案 11 :(得分:0)

对我来说,这是因为我在 @BeforeAll 方法中存根了模拟。

MockitoExtension 没有 @BeforeAll 的回调。

public class MockitoExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver

我将存根移到了它工作的测试方法中!!

答案 12 :(得分:0)

使用 @ExtendWith(MockitoExtension.class) 注释测试类。

答案 13 :(得分:0)

在我的情况下,这是由于@Test注释的导入错误

确保您正在使用以下导入

import org.junit.jupiter.api.Test;

答案 14 :(得分:0)

由于这是我发现的与问题最接近的结果,因此是第一个结果,但我没有找到合适的答案,因此我将在此处发布解决方案,以解决将来任何可怜的人:

any()在模拟类方法使用原始参数的情况下不起作用。

 public Boolean getResult(String identifier, boolean switch)

以上内容将产生与OP完全相同的问题。

解决方案,只需将其包装:

 public Boolean getResult(String identifier, Boolean switch)

后者解决了NPE。

答案 15 :(得分:0)

另一个常见的陷阱是方法签名被意外声明为“最终”。

这个人吸引了很多使用Checkstyle进行代码库开发的人员,并且内部化了将成员标记为final的需要。

即在OP的示例中:

object.method()

确保未将method()声明为final

public final Object method() {
}

Mockito无法模拟最终方法,它将作为包装的NPE出现:

Suppressed: org.mockito.exceptions.misusing.InvalidUseOfMatchersException:

在错误消息中深入了解以下内容:

Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.

答案 16 :(得分:0)

使用JUnit 5或更高版本时。您必须注入用@Mock注释的类 在@BeforeEach设置中。

答案 17 :(得分:0)

以上答案均无济于事。我在努力理解为什么代码可以在Java中工作而在Kotlin中不能工作。

然后我从this thread中找出了答案。

您必须创建类和成员函数open,否则将引发NPE。

完成功能open后,测试开始通过。

您最好考虑使用编译器的"all-open" plugin

  

Kotlin具有类及其成员,默认情况下为 final ,这使得使用Spring AOP等要求类开放<的框架和库变得不方便/ strong>。 all-open编译器插件使Kotlin适应了这些框架的要求,并使带有特定注释的类被注释,并且其成员打开而没有明确的open关键字

答案 18 :(得分:0)

这些答案都不对我有用。这个答案不能解决OP的问题,但是由于这篇文章是唯一出现在Google搜索此问题的文章,所以我在这里分享我的答案。

在编写Android单元测试时遇到了这个问题。问题是我正在测试的活动扩展了AppCompatActivity而不是Activity。为了解决这个问题,我可以将AppCompatActivity替换为Activity,因为我并不是真的需要它。这可能不是每个人都可行的解决方案,但希望知道根本原因会有所帮助。

答案 19 :(得分: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)

答案 20 :(得分:0)

爱德·韦伯(Ed Webb)的answer帮助了我。而且,您也可以尝试添加

else

如果您 @Rule public Mocks mocks = new Mocks(this);

答案 21 :(得分:0)

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

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

@InjectMocks
private exampleServiceImpl exampleServiceMock;

而不是:

@Mock
private exampleService exampleServiceMock;