Reputation: 60
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
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:
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
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
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