我在这里找到了一个类似的问题:Mockito when()...then() NullPointerException 但在那个问题中,待测试的方法是静态的。
我有一个带有待测试的布尔方法的类。测试类在when().. then()行上给出NULLPOINTEREXCEPTION。
感谢您的帮助。
要测试的课程
public class FMBaseController implements FMHandler {
private PlayerHandler player;
public FMBaseController(PlayerHandler player) {
this.player = player;
}
@Override
public boolean boostValue(FamilyMember fm, int increase) {
if (player.getResourceHandler().getServants() < increase)
return false;
return true;
经过测试的课程
public class FMBaseControllerTest {
//class to test
private FMBaseController fmBase;
//dependencies (these will be mocked)
private FamilyMember fm;
private PlayerHandler playerHandler;
@Before
public void setUp() throws Exception {
fm = mock(FamilyMember.class);
playerHandler = mock(PlayerHandler.class);
fmBase = new FMBaseController(playerHandler);
}
@Test
public void boostValueTest() {
when(playerHandler.getResourceHandler().getServants()).thenReturn(3).thenReturn(5);
//3 is less than 4 . assert you cannot boost
Boolean bool1 = fmBase.boostValue(fm, 4);
assertFalse( bool1 );
//5 is not less than 4 . assert you can boost
Boolean bool2 = fmBase.boostValue(fm, 4);
assertTrue( bool2 );
}
}
未能跟踪
FMBaseControllerTest
******.server.controller.FMBaseControllerTest
boostValueTest(******.server.controller.FMBaseControllerTest)
java.lang.NullPointerException
at ******.server.controller.FMBaseControllerTest.boostValueTest(FMBaseControllerTest.java:44)
答案 0 :(得分:2)
你需要将它添加到boostValueTest:
ResourceHandler resourceHandler = mock(resourceHandler.class);
when(playerHandler.getResourceHandler()).thenReturn(resourceHandler);
致电时:
playerHandler.getResourceHandler().getServants()
playerHandler.getResourceHandler()
- 返回null,因为null是mockito中未模拟的所有内容的默认结果。
并且您的测试方法是绿色的:
@Test
public void boostValueTest() {
Servants servants = mock(Servants.class);
when(playerHandler.getResourceHandler()).thenReturn(servants);
when(playerHandler.getResourceHandler().getServants()).thenReturn(3).thenReturn(5);
//3 is less than 4 . assert you cannot boost
Boolean bool1 = fmBase.boostValue(fm, 4);
assertFalse( bool1 );
//5 is not less than 4 . assert you can boost
Boolean bool2 = fmBase.boostValue(fm, 4);
assertTrue( bool2 );
}
答案 1 :(得分:1)
这是因为Mockito的默认行为,在docs:
中描述默认情况下,对于返回值的所有方法,mock将根据需要返回null,基元/原始包装器值或空集合。例如0表示int / Integer,false表示布尔值/布尔值。
将您的调用作为示例playerHandler.getResourceHandler().getServants()
,playerHandler
模拟没有getResourceHandler()
方法的指定行为,因此它将返回null
,因此{{1}导致NPE。
您至少可以使用以下两种方式更改此内容:
null.getServants()
:ResourceHandler
可以覆盖Stubbing:例如,常见的存根可以进入夹具设置,但测试方法可以覆盖它。请注意,覆盖存根是一种潜在的代码气味,指出太多的存根
ResourceHandler resourceHandler = mock(resourceHandler.class);
when(playerHandler.getResourceHandler()).thenReturn(resourceHandler);
或者,如果您正在使用注释:
playerHandler = mock(PlayerHandler.class, RETURNS_DEEP_STUBS);
尽管如此,虽然它有时很有用,但您还应该考虑到:
警告:常规清洁代码很少需要此功能!留下遗留代码。嘲笑一个模拟器来返回一个模拟器,返回一个模拟器,(...),返回违反Demeter法则的一些有意义的提示或者模拟一个值对象(一个众所周知的反模式)。
好的引用我在网上看到过一天:每次模拟返回模拟时,仙女死了。
请注意,此答案将返回与存根匹配的现有模拟。对于深层存根,此行为是正常的,并允许验证在链的最后一个模拟上工作。
答案 2 :(得分:0)
通过PowerMockito
可以更简单地完成。
但首先,我认为将playerHandler.getResourceHandler(). getServants()
提取到私有方法getServants()
会更好,因为它看起来不够好。
public class FMBaseController implements FMHandler {
private PlayerHandler player;
public FMBaseController(PlayerHandler player) {
this.player = player;
}
@Override
public boolean boostValue(FamilyMember fm, int increase) {
return getServants() >= increase;
}
private int getServants() {
return player.getResourceHandler().getServants();
}
}
然后,您可以使用PowerMockito
和@RunWith
类注释在@PrepareForTest
之前存根此私有方法。
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;
import static org.powermock.api.mockito.PowerMockito.verifyPrivate;
@RunWith(PowerMockRunner.class)
@PrepareForTest(FMBaseController.class)
public class FMBaseControllerTest {
private PlayerHandler playerHandler;
private FamilyMember fm;
private FMBaseController fmBaseController;
@Before
public void setUp() throws Exception {
fm = mock(FamilyMember.class);
playerHandler = mock(PlayerHandler.class);
fmBaseController = spy(new FMBaseController(playerHandler));
}
@Test
public void boostValue() throws Exception {
PowerMockito.doReturn(3, 4).when(fmBaseController, "getServants");
assertFalse(fmBaseController.boostValue(fm, 4));
assertTrue(fmBaseController.boostValue(fm, 4));
verifyPrivate(fmBaseController, times(2)).invoke("getServants");
}
}