Mockito:使用when().. then()测试布尔方法

时间:2017-07-01 16:02:52

标签: java unit-testing junit mockito

我在这里找到了一个类似的问题: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)

3 个答案:

答案 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。

您至少可以使用以下两种方式更改此内容:

  • 定义您的方法的行为,因为@sbjavateam建议返回另一个模拟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");
    }
}