LoSpazzino
LoSpazzino

Reputation: 60

Mockito : Testing boolean method with when()..then()

I found a similar question here: Mockito when()...then() NullPointerException but in that question method to be tested was static.

I have a class with a boolean method to be tested. The test class gives NULLPOINTEREXCEPTION on the when()..then() line.

Thanks for your help.

Class to be tested

   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;

Tested class

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

FAILURE TRACE

FMBaseControllerTest
******.server.controller.FMBaseControllerTest
boostValueTest(******.server.controller.FMBaseControllerTest)
java.lang.NullPointerException

    at ******.server.controller.FMBaseControllerTest.boostValueTest(FMBaseControllerTest.java:44)

Upvotes: 2

Views: 23032

Answers (3)

Morfic
Morfic

Reputation: 15528

This happens because of the default behaviour of Mockito, described in the docs:

By default, for all methods that return a value, a mock will return either null, a primitive/primitive wrapper value, or an empty collection, as appropriate. For example 0 for an int/Integer and false for a boolean/Boolean.

Taking your call as an example playerHandler.getResourceHandler().getServants(), the playerHandler mock has no specified behaviour for the getResourceHandler() method, so it will return null, hence null.getServants() results in a NPE.

You can change this in at least the following 2 fashions:

  • define a behaviour for your method as @sbjavateam suggested to return another mock ResourceHandler:
    ResourceHandler resourceHandler = mock(resourceHandler.class);
    when(playerHandler.getResourceHandler()).thenReturn(resourceHandler); 

Stubbing can be overridden: for example common stubbing can go to fixture setup but the test methods can override it. Please note that overridding stubbing is a potential code smell that points out too much stubbing

    playerHandler = mock(PlayerHandler.class, RETURNS_DEEP_STUBS);

    or, if you're using annotations:

   @Mock(answer = Answers.RETURNS_DEEP_STUBS)
   PlayerHandler playerHandler;

Nonetheless, while it's sometimes useful to do it, you should also take into account that:

WARNING: This feature should rarely be required for regular clean code! Leave it for legacy code. Mocking a mock to return a mock, to return a mock, (...), to return something meaningful hints at violation of Law of Demeter or mocking a value object (a well known anti-pattern).

Good quote I've seen one day on the web: every time a mock returns a mock a fairy dies.

Please note that this answer will return existing mocks that matches the stub. This behavior is ok with deep stubs and allows verification to work on the last mock of the chain.

Upvotes: 1

xyz
xyz

Reputation: 5427

you need to add this into boostValueTest :

ResourceHandler resourceHandler = mock(resourceHandler.class);
when(playerHandler.getResourceHandler()).thenReturn(resourceHandler);  

when you call :

playerHandler.getResourceHandler().getServants()

playerHandler.getResourceHandler() - returns null , as null is default result for all not mocked in mockito.

and your test method is green:

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

Upvotes: 2

Vanguard
Vanguard

Reputation: 1396

By PowerMockito it could be done simpler.

But first of all I think it is better if your extract playerHandler.getResourceHandler(). getServants() to private method getServants(), because it doesn't look pretty good enough.

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

Then you may stub the this private method by PowerMockito by using @RunWith and @PrepareForTest class annotations.

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

Upvotes: 0

Related Questions