![](/img/trans.png)
[英]NullPointerException when stubbing a test method call with Mockito
[英]Mockito - NullpointerException when stubbing Method
所以我开始为我们的 Java-Spring-项目编写测试。
我使用的是 JUnit 和 Mockito。据说,当我使用 when()...thenReturn() 选项时,我可以模拟服务,而无需模拟它们。 所以我想做的是设置:
when(classIwantToTest.object.get().methodWhichReturnsAList(input))thenReturn(ListcreatedInsideTheTestClass)
但无论我执行哪个 when 子句,我总是得到 NullpointerException,这当然是有道理的,因为输入是 null。
此外,当我尝试从 object 模拟另一种方法时:
when(object.method()).thenReturn(true)
在那里我也得到一个 Nullpointer,因为该方法需要一个未设置的变量。
但是我想使用 when()..thenReturn() 来绕过创建这个变量等等。 我只是想确保,如果任何 class 调用此方法,那么无论如何,只需返回 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);
}
我遇到了这个问题,我的问题是我使用any()
而不是anyInt()
调用我的方法。 所以我有:
doAnswer(...).with(myMockObject).thisFuncTakesAnInt(any())
我不得不将其更改为:
doAnswer(...).with(myMockObject).thisFuncTakesAnInt(anyInt())
我不知道为什么会产生 NullPointerException。 也许这会帮助下一个可怜的灵魂。
对于布尔方法,尚未存根的方法的默认返回值为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,它更详细地解释了这一点,并且还有一些关于其使用的警告。
我希望这有帮助。 请注意,您的示例代码似乎有更多问题,例如缺少断言或验证语句以及在模拟上调用设置器(这没有任何效果)。
我遇到了同样的问题,我的问题只是我没有使用 @RunWith 正确注释类。 在您的示例中,请确保您拥有:
@RunWith(MockitoJUnitRunner.class)
public class Test {
...
一旦我这样做了,NullPointerExceptions 就消失了。
对于未来的读者来说,使用模拟时 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)
对我来说,我获得 NPE 的原因是我在模拟原语时使用Mockito.any()
。 我发现通过切换到使用来自 mockito 的正确变体可以消除错误。
例如,要模拟一个将原始long
作为参数的函数,而不是使用any()
,您应该更具体并将其替换为any(Long.class)
或Mockito.anyLong()
。
希望对某人有所帮助。
确保你初始化你的模拟。
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)
由于这是我发现的与我遇到的问题最接近的结果,因此这是出现的第一个结果,但我没有找到合适的答案,我将在此处为任何未来的可怜人发布解决方案:
any()
在模拟类方法使用原始参数的情况下不起作用。
public Boolean getResult(String identifier, boolean switch)
以上将产生与OP相同的问题。
解决方案,只需包装它:
public Boolean getResult(String identifier, Boolean switch)
后者解决了NPE。
角落案例:
如果您使用Scala并尝试在value 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 问题更具体地说明了这一点,但是当您不知道问题出在值类上时,您会错过它。
您需要初始化 MockitoAnnotations.initMocks(this) 方法必须调用以初始化带注释的字段。
@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
有关更多详细信息,请参阅文档
对于 JUnit 5,测试类必须使用以下注释:
@ExtendWith(MockitoExtension.class)
进口:
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
我的问题已通过此添加解决。
另一个常见的问题是方法签名被意外声明为“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.
对我来说,这是因为我在@BeforeAll
方法中存根模拟。
MockitoExtension
没有@BeforeAll
的回调。
public class MockitoExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver
我在它工作的测试方法中移动了存根!
就我而言,Intellij 使用org.junit.jupiter.api.Test
(Junit5) 而不是 import org.junit.Test
of (Junit4) 创建了 Test,这导致所有 bean 显然都为空。 另外,确保类和测试方法是公开的
@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 to Context mockContext;
@Mock
Context mockContext;
但是,如果您仅使用@RunWith(MockitoJUnitRunner.class)它将起作用。 根据 Mockito,您可以使用@Mock 或 Mockito.mock(Context.class);创建模拟对象。 ,
由于使用了@RunWith(PowerMockRunner.class),我得到了 NullpointerException,而不是我改为@RunWith(MockitoJUnitRunner.class) 它工作正常
就我而言,我错过了 add first
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)
那么在我的情况下,这是因为错误的注释使用。 我使用 junit 4 进行测试,并在初始化时使用@BeforeEach
而不是@Before
。
将其更改为@Before
,它就像魅力一样。
就我而言, when()
的导入错误。
我偶然使用了import static reactor.core.publisher.Mono.when
。
Ed Webb 的回答对我有帮助。 相反,您也可以尝试添加
@Rule public Mocks mocks = new Mocks(this);
如果你@RunWith(JUnit4.class)
。
这些答案都不适合我。 这个答案并不能解决 OP 的问题,但由于这篇文章是唯一出现在谷歌搜索这个问题的帖子,所以我在这里分享我的答案。
我在为 Android 编写单元测试时遇到了这个问题。 问题是我正在测试的活动扩展了AppCompatActivity
而不是Activity
。 为了解决这个问题,我可以将AppCompatActivity
替换为Activity
,因为我真的不需要它。 这对每个人来说可能不是一个可行的解决方案,但希望知道根本原因会对某人有所帮助。
使用 JUnit 5 或更高版本时。 您必须在@BeforeEach
设置中注入带有@Mock
注释的类。
在我的情况下,这是由于 @Test 注释的错误导入
确保您使用的是以下导入
import org.junit.jupiter.api.Test;
使用@ExtendWith(MockitoExtension.class)
注释测试类。
当我在 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
。 我没有检查它并改变了一个图书馆,对不起。
我试图模拟一个“最终”方法,这显然是问题所在。
处理此问题的正确方法是使用接口并模拟该接口,但是我无法控制“最终”方法所在的库。
Mockito 2 可以处理模拟 final 方法。 在项目的 src/test/resources/mockito-extensions 目录中添加一个名为 org.mockito.plugins.MockMaker 的文本文件并添加一行文本:
mock-maker-inline
之后,模拟最终方法应该可以正常工作。
检查您使用的 Junit 版本。 在 Maven/Gradle 构建工具中,如果您设置使用testRuntimeOnly 'junit5'
,那么它可能不会使用@RunWith
,因为它不可用并且在 Junit5 中它被替换为@ExtendWith
。
这不回答 OP 的原始查询,但在这里尝试帮助其他人解决 Mockito null 指针异常(NPE)。
我的 NPE 正在发生,因为我没有将测试依赖项下的 class 明确设置为我模拟的类。 因此,被测 class 无法找到其所需的依赖项,从而创建了 NPE。 我倾向于不模拟正在测试的 class(即使用new
关键字),以确保我得到我的原生 class 行为进行测试。
由于我无法控制的原因,我仍在使用 Junit 4。 工作示例;
待测类
public class ClassUnderTest {
private DependantClassOne dependantClassOne;
private DependantClassTwo dependantClassTwo;
// remaining class, including setters
}
测试 Class
@RunWith(MockitoJUnitRunner.class)
public class Test {
private ClassUnderTest classUnderTest;
private DependantClassOne dependantClassOne;
private DependantClassTwo dependantClassTwo;
@Before
public void setup() {
dependantClassOne = mock(DependantClassOne.class);
dependantClassTwo = mock(DependantClassTwo.class);
classUnderTest = new ClassUnderTest();
classUnderTest.setDependantClassOne(dependantClassOne); //added to prevent NPE
classUnderTest.setDependantClassTwo(dependantClassTwo); //added to prevent NPE
}
// tests
}
就我而言,我的 Mockito 注释与 JUnit 版本不匹配。
使用@ExtendWith(MockitoExtension.class)
时,请确保您使用的是 JUnit 5: import org.junit.jupiter.api.Test;
使用@RunWith(MockitoJUnitRunner.class)
时,请确保您使用的是 JUnit 4: import org.junit.Test;
在我的案例中,UnderTest class 有两种注入:通过构造函数 (TheSecondService) 和通过设置器 (TheFirstService)。
通过 setter 进行的所有注入都是 null。这有助于:
@Mock
private TheFirstService theFirstService;
@Mock
private TheSecondService theSecondService;
@InjectMocks
private TheThirdService underTest;
@BeforeEach
public void setup() {
underTest.setTheFirstService(theFirstService);
}
面临同样的问题,对我有用的解决方案:
我没有模拟服务接口,而是使用 @InjectMocks 来模拟服务实现:
@InjectMocks
private exampleServiceImpl exampleServiceMock;
代替 :
@Mock
private exampleService exampleServiceMock;
就我而言,我在映射器声明中缺少注释@mock
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.