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

14,405

Solution 1

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

Solution 2

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.

Share:
14,405
LoSpazzino
Author by

LoSpazzino

Trying to learn how to code

Updated on June 04, 2022

Comments

  • LoSpazzino
    LoSpazzino almost 2 years

    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)
    
  • LoSpazzino
    LoSpazzino almost 7 years
    Thank you very much! It works. How would i test this else always in the same method ? With a verify? else { player.getResourceHandler().payServants(increase); fm.setValue(fm.getValue() + increase); return true; }
  • LoSpazzino
    LoSpazzino almost 7 years
    It's helpful to know that payServants is a void method
  • xyz
    xyz almost 7 years
    yes. with verify(). do fo verify you also should add when . like doNothing().when(payServants(any() or value )..... verify(resourceHandler).payServants(any() or value)
  • LoSpazzino
    LoSpazzino almost 7 years
    I'll add this here if someone later comes:
  • LoSpazzino
    LoSpazzino almost 7 years
    doNothing().when(resourceHandler).payServants(Mockito.anyInt‌​()); verify(resourceHandler).payServants(Mockito.anyInt());
  • LoSpazzino
    LoSpazzino almost 7 years
    Thank you! My classes are full of deep stubs, I guess it's too late to change it now