Mockito-存根方法时发生NullpointerException


79

因此,我开始为我们的Java Spring项目编写测试。

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

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

但是无论我执行哪一个子句,总会得到NullpointerException,这当然是有道理的,因为input为null。

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

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

在那里,我还得到了一个N​​ullpointer,因为该方法需要一个未设置的变量。

但是我想使用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);


}

3
when()中的对象必须是由Mockito.mock()创建的模拟,它不适用于手动创建的真实对象-不确定这是否是您的问题,因为我不太了解您的问题所在...
Nadir

1
请发布一些代码。
Michael Lloyd Lee mlk 2015年

1
听起来classIwantToTest.object.get()结果不是模拟,或者get()返回null。但是请发布您的代码,显示您的测试以及模拟的初始化。
vikingsteve

添加了一些代码。需要更改变量名,因为它来自我公司;)。
user5417542 2015年

请在下面引用我的答案
阿南德·克里希(Andy Krish),

Answers:


61

您尚未存根的方法的默认返回值是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(这没有任何作用)。


在哪里可以找到答案第一句话的说明/文档?谢谢!
Sturzl


176

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

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

我不得不将其更改为:

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

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


51
这对我有用。将这个评论留给下一个可怜的灵魂。
Pavan Kumar”,

49
匹配器无法正常工作的原语,因此,如果该参数是一个原始的,你会需要的anyChar / INT /布尔等

16
不允许any()所有变量都起作用,使其表现为NPE,就等于对开发团队进行了网络战攻击。
还没检查过

原因是any()返回通用对象引用。该对象未装箱为整数,因此泛型被确定为“整数”。不过,该对象为null,因此取消装箱操作无法访问null指针。anyInt()提供一个本机整数,因此可以正常工作。
爱德华

将any()更改为anyInt()也为我解决了它-谢谢!(我的案例涉及Java测试和Kotlin类)
Maggnetix

49

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

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

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


1
我得到了@RunWith(PowerMockRunner.class)的空指针bcoz,而不是我改成了@RunWith(MockitoJUnitRunner.class)。
阿南德·克里什(Anand krish),

20

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


1
@BeforeEach在这里帮助了我,但它始终取决于执行上下文。
Mateusz Gebroski,

1
+1表示“确保所有注释都使用JUnit”!Intellij以某种方式假定我要使用它,org.junit.jupiter.api.Test而不是使模拟import org.junit.Test显然为null。
Sayadi

我以某种方式错过了这一行“ MockitoAnnotations.initMocks(this);”
Ajay Kedare

就我而言,我忘记了有效地初始化变量,谢谢!
匿名

15

对我来说,获得NPE的原因是我Mockito.any()在模拟原语时使用的是NPE 。我发现通过切换到使用来自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.
Aaron

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)

5

极端情况:
如果您使用的是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问题是关于此的更具体的问题,但是当您不知道值类的问题时,您会错过它。


3

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

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

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


使用org.junit.jupiter.api.BeforeEachinsted of@Before帮助了我。
RAM237

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 Annotation来标记,则不需要上面所有注释的代码{ }Context mockContext;

@Mock 
Context mockContext; 

但是,如果仅使用@RunWith(MockitoJUnitRunner.class),它将起作用。按照Mockito,您可以使用@Mock或Mockito.mock(Context.class);创建模拟对象

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


0

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

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

@InjectMocks
private exampleServiceImpl exampleServiceMock;

代替 :

@Mock
private exampleService exampleServiceMock;

0

埃德·韦伯的回答对我来说很有帮助。而且,您也可以尝试添加

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

0

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

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


0

上面的答案都没有帮助我。我一直在努力理解为什么代码可以在Java中工作,但不能在Kotlin中工作。

然后我从这个线程中找出来

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

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

您不妨考虑使用编译器的“全开”插件

Kotlin默认情况下有类及其成员为final,这使得使用要求打开类的框架和库(如Spring AOP)变得不便。该all-open编译器插件适应科特林到这些框架的要求,并使得与特定的注解类及其成员打开没有明确的开放关键字。


0

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


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.

0

因为这是我发现的最接近的问题,所以这是第一个结果,我没有找到合适的答案,我将在这里为将来的可怜人发布解决方案:

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

 public Boolean getResult(String identifier, boolean switch)

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

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

 public Boolean getResult(String identifier, Boolean switch)

后者解决了NPE。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.