Mockito - 存根方法时的NullpointerException

use*_*542 49 java testing junit mockito stubbing

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

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

when(classIwantToTest.object.get().methodWhichReturnsAList(input))thenReturn(ListcreatedInsideTheTestClass)  
Run Code Online (Sandbox Code Playgroud)

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

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

when(object.method()).thenReturn(true)
Run Code Online (Sandbox Code Playgroud)

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

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

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

码:

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();
        }   
}
Run Code Online (Sandbox Code Playgroud)

这是我的测试类:

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);


}
Run Code Online (Sandbox Code Playgroud)

Rya*_*ton 98

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

doAnswer(...).with(myMockObject).thisFuncTakesAnInt(any())
Run Code Online (Sandbox Code Playgroud)

我不得不把它改成:

doAnswer(...).with(myMockObject).thisFuncTakesAnInt(anyInt())
Run Code Online (Sandbox Code Playgroud)

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

  • 这对我有用.留下这个评论为下一个可怜的灵魂. (30认同)
  • Matchers不适用于原语,所以如果参数是原语,你将需要anyChar/Int/Boolean等. (25认同)
  • 不允许“ any()”对所有变量起作用,并将其表现为NPE就是对开发团队的网络战攻击。 (4认同)
  • 原因是 any() 返回一个通用对象引用。该对象被拆箱为整数,因此泛型类型被确定为“整数”。尽管该对象为空,因此拆箱无法访问空指针。anyInt() 提供了一个原生整数,所以它可以工作。 (3认同)
  • “这对我有用。将此评论留给下一个可怜的灵魂。” ...... 5年过去了...... 我是下一个可怜的灵魂哈哈。 (3认同)

ahu*_*ahu 49

您尚未存根的方法的默认返回值是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);
Run Code Online (Sandbox Code Playgroud)

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

SomeService myService = mock(SomeService.class, Mockito.RETURNS_DEEP_STUBS);
when(myService.getListWithData().get()).thenReturn(item1);
Run Code Online (Sandbox Code Playgroud)

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

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

  • https://static.javadoc.io/org.mockito/mockito-core/2.20.0/org/mockito/Mockito.html#RETURNS_DEFAULTS或查看源代码:https://github.com/mockito/mockito /blob/v2.20.0/src/main/java/org/mockito/internal/stubbing/defaultanswers/ReturnsEmptyValues.java#L20-L49 (2认同)

小智 43

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

@RunWith(MockitoJUnitRunner.class)
public class Test {
...
Run Code Online (Sandbox Code Playgroud)

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

  • 使用 Junit 5 遇到同样的问题,我使用 @ExtendWith(MockitoExtension.class) 而不是 @RunWith(MockitoJUnitRunner.class) 解决它。 (3认同)
  • 我得到了@RunWith(PowerMockRunner.class) 的空指针bcoz,而不是我改为@RunWith(MockitoJUnitRunner.class)。 (2认同)

Tan*_*nel 20

由于这是我发现的最接近我遇到的问题的结果,这是出现的第一个结果,而且我没有找到合适的答案,我将在这里为未来的可怜的灵魂发布解决方案:

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

 public Boolean getResult(String identifier, boolean switch)
Run Code Online (Sandbox Code Playgroud)

以上将产生与 OP 相同的问题。

解决方案,只需将其包裹起来:

 public Boolean getResult(String identifier, Boolean switch)
Run Code Online (Sandbox Code Playgroud)

后者解决了NPE。

  • 请记住,如果您选择这种方法,现在您可能希望在生产代码中包含布尔值的空检查(来源:由 Ridcully 提出)

  • 这正是发生在我身上的事情。 (2认同)
  • 或者更好的是,您可以将 any() 替换为 anyBoolean() 并将原语保留在代码中...... (2认同)

gar*_*may 11

确保你初始化你的模拟。

JUnit4@Before

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}
Run Code Online (Sandbox Code Playgroud)

JUnit5@BeforeEach

@BeforeEach
public void setup() {
    MockitoAnnotations.initMocks(this);
}
Run Code Online (Sandbox Code Playgroud)

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

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

@RunWith(MockitoJUnitRunner.class)
Run Code Online (Sandbox Code Playgroud)


Tal*_*ffe 9

对于将来的读者,使用模拟时导致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");
   // ...
}
Run Code Online (Sandbox Code Playgroud)

还要确保所有注释都使用JUnit。我曾经不小心用testNG的@Test创建了一个测试,所以@Before不能使用它(在testNG中,注释是@BeforeTest)

  • +1 表示“确保您使用 JUnit 进行所有注释”!不知何故,Intellij 假设我想使用“org.junit.jupiter.api.Test”而不是“import org.junit.Test”,这导致模拟显然为空。 (5认同)

sma*_*c89 8

对我来说,获得NPE的原因是我Mockito.any()在模拟基元时使用了NPE 。我发现通过切换到使用来自Mockito的正确变体可以消除错误。

例如,要模拟以原始类型long为参数的函数,而不是使用any(),应更加具体,并用any(Long.class)或替换Mockito.anyLong()

希望能对某人有所帮助。

  • 谢谢你!!!!我收到了没有意义的错误,但使用 Mockito.anyInt() 而不是 Mockito.any() 解决了它。错误示例:“org.mockito.exceptions.misusing.InvalidUseOfMatchersException:检测到错误放置或误用的参数匹配器...您不能在验证或存根之外使用参数匹配器。” (2认同)

Vit*_*uel 7

特殊情况:
如果您使用Scala并尝试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])
   ...
}
Run Code Online (Sandbox Code Playgroud)

另一个SO 问题更具体地说明了这一点,但是当您不知道问题与值类有关时,您就会错过它。


Pou*_*Pal 7

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

@ExtendWith(MockitoExtension.class)
Run Code Online (Sandbox Code Playgroud)

进口:

import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
Run Code Online (Sandbox Code Playgroud)

我的问题通过此添加得到解决。


يعق*_*قوب 6

您需要初始化 MockitoAnnotations.initMocks(this) 方法来初始化带注释的字段。

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请参阅文档


8bi*_*kie 5

检查方法签名是否未声明为 final

这个问题引起了很多从事代码库工作的人的注意,这些代码库受 Checkstyle 的约束,并且已经内化了将成员标记为final.

即在OP的例子中:

object.method()

确保method()未声明为final

public final Object method() {
}
Run Code Online (Sandbox Code Playgroud)

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

Suppressed: org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Run Code Online (Sandbox Code Playgroud)

错误消息中隐藏着以下内容:

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.
Run Code Online (Sandbox Code Playgroud)