Mockito - 存根方法时出现 NullpointerException

Posted

技术标签:

【中文标题】Mockito - 存根方法时出现 NullpointerException【英文标题】:Mockito - NullpointerException when stubbing Method 【发布时间】:2016-01-12 11:19:48 【问题描述】:

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

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

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

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

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

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

我还得到了一个 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();
           

这是我的测试课:

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



【问题讨论】:

when() 中的对象必须是由 Mockito.mock() 创建的模拟,它不适用于手动创建的真实对象 - 不确定这是否是您的问题,因为我并没有真正了解你的问题出在哪里…… 请贴一些代码。 听起来 classIwantToTest.object.get() 结果不是模拟,或者 get() 返回 null。但请发布您的代码,展示您的测试和模拟的初始化。 添加了一些代码。需要更改变量名,因为它来自我的公司;)。 在下面参考我的答案 【参考方案1】:

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

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

我不得不将其更改为:

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

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

【讨论】:

这对我有用。将此评论留给下一个可怜的灵魂。 匹配器不适用于原语,因此如果参数是原语,您将需要 anyChar/Int/Boolean 等。 不允许 any() 处理所有变量并将其表现为 NPE 无异于对开发团队的网络战攻击。 原因是 any() 返回一个通用对象引用。该对象被拆箱为整数,因此泛型类型被确定为“整数”。但是该对象为空,因此拆箱无法访问空指针。 anyInt() 提供了一个原生整数,所以它可以工作。 将 any() 更改为 anyInt() 也为我解决了这个问题 - 谢谢! (我的案例涉及 java 测试和 kotlin 类)【参考方案2】:

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

【讨论】:

在哪里可以找到答案第一句的规范/文档?谢谢! static.javadoc.io/org.mockito/mockito-core/2.20.0/org/mockito/… 或者看一下源码:github.com/mockito/mockito/blob/v2.20.0/src/main/java/org/…【参考方案3】:

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

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

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

【讨论】:

我得到了@RunWith(PowerMockRunner.class)的空指针bcoz,而不是我改为@RunWith(MockitoJUnitRunner.class)。 对于 Kotlin:@RunWith(MockitoJUnitRunner::class) 在使用 Junit 5 时遇到同样的问题,我使用以下方法解决它:@ExtendWith(MockitoExtension.class) 而不是 @RunWith(MockitoJUnitRunner.class)。【参考方案4】:

对于未来的读者来说,使用模拟时 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)

【讨论】:

@BeforeEach 在这里帮助了我,但它始终取决于执行上下文。 +1 表示“确保所有注释都使用 JUnit”!不知何故,Intellij 假设我想使用 org.junit.jupiter.api.Test 而不是 import org.junit.Test 这导致模拟显然为空。 我不知何故错过了这一行“MockitoAnnotations.initMocks(this);” 就我而言,我忘了有效地初始化我的变量谢谢!!【参考方案5】:

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

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

希望对某人有所帮助。

【讨论】:

谢谢!!!!我遇到了没有意义的错误,但使用 Mockito.anyInt() 而不是 Mockito.any() 解决了它。示例错误:org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Misplaced or misused argument matcher detected... You cannot use argument matchers outside of verification or stubbing.【参考方案6】:

确保初始化模拟。

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)

【讨论】:

initMocks 已弃用,请改用 MockitoAnnotations.openMocks 我使用的是 JUnit 版本 4,我仍然需要 "@RunWith(MockitoJUnitRunner.class)" 注释才能使 "when" 方法正常工作。【参考方案7】:

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

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

 public Boolean getResult(String identifier, boolean switch)

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

解决办法,把它包起来:

 public Boolean getResult(String identifier, Boolean switch)

后者解决了NPE。

【讨论】:

这正是发生在我身上的事。 寻找和解决方案【参考方案8】:

角盒: 如果您使用 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])
   ...

另一个SO question 更具体地说明了这一点,但如果您不知道问题出在值类上,您就会错过它。

【讨论】:

【参考方案9】:

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

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

更多详情见Doc

【讨论】:

使用org.junit.jupiter.api.BeforeEach insted of @Before 帮助了我。【参考方案10】:

对于 JUnit 5,测试类必须使用以下注释:

@ExtendWith(MockitoExtension.class)

进口:

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

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

【讨论】:

【参考方案11】:

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

这吸引了很多从事 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.

【讨论】:

【参考方案12】:

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

MockitoExtension 没有@BeforeAll 的回调。

public class MockitoExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver

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

【讨论】:

【参考方案13】:

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

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)

【讨论】:

【参考方案14】:
@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);, 如果你对Context mockContext;使用@Mock注解

@Mock 
Context mockContext; 

但如果你只使用 @RunWith(MockitoJUnitRunner.class) 它将起作用。根据 Mockito,您可以使用 @Mock 或 Mockito.mock(Context.class);

创建模拟对象

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

【讨论】:

【参考方案15】:

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

然后我从this thread弄明白了。

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

在制作函数open后,测试开始通过。

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

Kotlin 具有类及其成员默认为final,这使得使用框架和库(例如 Spring AOP)需要开放。 all-open 编译器插件使 Kotlin 适应这些框架的要求,并使带有特定注释的类及其成员打开,而无需显式的 open 关键字。

【讨论】:

【参考方案16】:

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

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

【讨论】:

【参考方案17】:

在我的情况下,when() 的导入错误。

我偶然使用了import static reactor.core.publisher.Mono.when

【讨论】:

【参考方案18】:

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

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

如果你@RunWith(JUnit4.class).

【讨论】:

【参考方案19】:

这些答案都不适合我。这个答案不能解决 OP 的问题,但由于这篇文章是唯一出现在谷歌上搜索这个问题的帖子,所以我在这里分享我的答案。

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

【讨论】:

【参考方案20】:

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

【讨论】:

【参考方案21】:

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

确保您使用的是以下导入

import org.junit.jupiter.api.Test;

【讨论】:

【参考方案22】:

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

【讨论】:

【参考方案23】:

在我的情况下,我在映射器声明中缺少注释 @mock

【讨论】:

嗨艾夫斯。您想发表评论分享您的经验吗?请使用相关部分。 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于Mockito - 存根方法时出现 NullpointerException的主要内容,如果未能解决你的问题,请参考以下文章

java 无效方法在mockito中存根

使用 Mockito 将 Class<T> 作为参数的方法存根

如何在 JUnit Test Java 中替换 Mockito 以存根类

使用Mockito为Java 8 lambda表达式编写存根

在 Mockito 中检测到未完成的存根

将Mockito与IntelliJ的Java一起使用时出现IllegalStateException