Mockito - 存根方法时出现 NullpointerException

Mockito - NullpointerException when stubbing Method

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

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

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

但是无论我执行哪个 when-clause,我总是得到一个 NullpointerException,这当然是有道理的,因为输入是空的。

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

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

我还得到了一个 Nullpointer,因为该方法需要一个未设置的变量。

但是我想使用 when()..thenReturn() 来绕过创建这个变量等等。我只是想确保,如果任何 class 调用此方法,那么无论如何,只是 return 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();
        }   
}

这是我的测试 class:

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


}

您尚未存根的方法的默认 return 值对于布尔方法是 false,对于方法 returning 集合或映射和 null 否则。

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

一个选项是为所有中间 return 值创建模拟并在使用前存根。例如:

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

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

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

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

希望对您有所帮助。请注意,您的示例代码似乎有更多问题,例如缺少断言或验证语句以及在模拟上调用设置器(没有任何效果)。

我遇到了同样的问题,我的问题很简单,我没有使用@RunWith 正确注释 class。在你的例子中,确保你有:

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

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

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

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

我不得不将其更改为:

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

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

@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),它工作正常

极端情况:
如果您正在使用 Scala 并尝试在 值 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 更具体地说明了这一点,但如果您不知道问题的价值 classes,您就会错过它。

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

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

希望对某人有所帮助。

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

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

Ed Webb 的 帮助了我。相反,您也可以尝试添加

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

如果你@RunWith(JUnit4.class).

就我而言,我错过了先添加

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)

None 这些答案对我有用。这个答案没有解决 OP 的问题,但由于这个 post 是唯一出现在谷歌搜索这个问题上的答案,我在这里分享我的答案。

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

None 以上答案对我有帮助。我一直在努力理解为什么代码在 Java 中有效,但在 Kotlin 中

然后我从this thread.

算出来了

你必须创建 class 和成员函数 open,否则会抛出 NPE。

制作功能后 open 测试开始通过。

你不妨考虑使用编译器的"all-open" plugin:

Kotlin has classes and their members final by default, which makes it inconvenient to use frameworks and libraries such as Spring AOP that require classes to be open. The all-open compiler plugin adapts Kotlin to the requirements of those frameworks and makes classes annotated with a specific annotation and their members open without the explicit open keyword.

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

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

有关详细信息,请参阅 Doc

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

确保初始化模拟。

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)

另一个常见问题是方法签名被意外声明为 'final'。

这个问题吸引了很多从事代码库工作的人,这些代码库受制于 Checkstyle,并且已经内化了将成员标记为 final 的需要。

即在 OP 的示例中:

object.method()

确保 method() 未声明为 final:

public final Object method() {
}

Mockito 无法模拟 final 方法,这将作为包装的 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.

因为这是我发现的最接近我遇到的问题的结果,所以这是出现的第一个结果,但我没有找到合适的答案,我会 post 这里的解决方案以供将来遇到任何问题灵魂:

any() 在模拟 class 方法使用原始参数的地方不起作用。

 public Boolean getResult(String identifier, boolean switch)

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

解决办法,直接包起来:

 public Boolean getResult(String identifier, Boolean switch)

后者解决了NPE

在我的例子中,这是由于错误导入了@Test 注解

确保您使用的是以下导入

import org.junit.jupiter.api.Test;

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

@ExtendWith(MockitoExtension.class)

进口:

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

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

将测试 class 注释为:@ExtendWith(MockitoExtension.class)

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

MockitoExtension 没有 @BeforeAll 的回调。

public class MockitoExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver

我将 stubbing 移到了它有效的测试方法中!!

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

将其更改为 @Before,效果非常好。

就我而言,这是 when() 的错误导入。

我不小心用了import static reactor.core.publisher.Mono.when

在我的例子中,我在映射器声明中缺少注释 @mock

在我的例子中,Intellij 使用 org.junit.jupiter.api.Test (Junit5) 而不是 import org.junit.Test of (Junit4) 创建了 Test,这显然导致所有 bean 都为空。 另外,确保 class 和测试方法是 public

这就是 google 带我去的地方,当时我有与 Junit 5 相同的 NullPointerException,但在我的 maven 项目中正确使用了 @ExtendWith(MockitoExtension.class)

结果我没有在我的 pom.xml 中包含 maven-surefire-plugin,这意味着 @ExtendWith 实际上没有做任何事情!

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-surefire-plugin</artifactId>
      <version>2.22.1</version>
    </plugin>
    ...

在我的例子中,一个测试方法调用另一个方法作为参数:

Mockito.`when`(repository.getItems(prefs.getUser().id)).thenReturn(listOf())`

虽然 repository 被模拟,但 prefs.getUser().id) 将抛出 NPE。所以,首先我们应该模拟一个参数,例如,

Mockito.`when`(prefs.getUser()).thenReturn(User(id = 1, name = "user"))`

我们也应该模拟prefs。没查到,换了个库,sorry

我试图模拟一个“最终”方法,这显然是问题所在。

处理此问题的正确方法是使用接口并模拟该接口,但我无法控制“最终”方法所在的库。

Mockito 2 可以处理模拟 final 方法。将文本文件添加到项目的 src/test/resources/mockito-extensions 目录,名为 org.mockito.plugins.MockMaker 并添加一行文本:

mock-maker-inline

在那之后,模拟 final 方法应该可以正常工作。

检查您使用的是哪个版本的 Junit。在 Maven/Gradle 构建工具中,如果您设置使用 testRuntimeOnly 'junit5',那么它可能不需要 @RunWith,因为它不可用,它在 Junit5 中被替换为 @ExtendWith