Search code examples
javatestingjunitmockito

AssertNotNull AssertionFailedError: expected: not <null>


I'm testing an app, where a method creates and object, and I'm trying to test that the object actually gets created like this:

   @Test
public void testCreateWagerGetsCreated(){
    DefaultSportsBettingService service = mock(DefaultSportsBettingService.class);
    Player player = mock(Player.class);
    Outcome outcome = new Outcome(null,"description",BigDecimal.valueOf(2),false);
    BigDecimal bd = BigDecimal.valueOf(100);
    assertNotNull(service.createWager(player,outcome, bd));
}

However the test fails throwing this error:

org.opentest4j.AssertionFailedError: expected: not <null>

    at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:39)
    at org.junit.jupiter.api.Assertions.fail(Assertions.java:134)
    at org.junit.jupiter.api.AssertNotNull.failNull(AssertNotNull.java:47)
    at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:36)
    at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:31)
    at org.junit.jupiter.api.Assertions.assertNotNull(Assertions.java:300)
    at com.epam.training.sportsbetting.Tester.testCreateWagerGetsCreated(Tester.java:64)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

I tried mocking the domain classes and instantiating them aswell, both throws the same error. What am I doing wrong?

Player class:

public class Player extends User{

private String name;

private BigDecimal balance;

private Currency currency;

public Player(){

}
public Player(String email, String password, String name, BigDecimal balance, Currency currency) {
    super(email, password);
    this.name = name;
    this.balance = balance;
    this.currency = currency;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public BigDecimal getBalance() {
    return balance;
}

public void setBalance(BigDecimal balance) {
    this.balance = balance;
}

public Currency getCurrency() {
    return currency;
}

public void setCurrency(Currency currency) {
    this.currency = currency;
}

@Override
public String toString() {
    return "Player{" +
            "name='" + name + '\'' +
            ", balance=" + balance +
            ", currency=" + currency +
            '}';
}

}

DefaultSportsBettingService class:

    public class DefaultSportsBettingService implements SportsBettingService{

public DummyDataStore dds = new DummyDataStore();
public List <Player> initializedPlayers = dds.getPlayerDatastore();
public List<User> initializedUsers = dds.getUserDataStore();
public List<Outcome> outcomeDatastore = dds.getOutcomeDatastore();
public List<SportEvent> sportEventDataStore = dds.getSportEventDataStore();
public List<Wager> wagerDatastore = dds.getWagerDatastore();
public List<Bet> betsDataStore = dds.getBetDataStore();
Player loggedPlayer;

@Override
public Player authenticateUser(User login) throws AuthenticationException {
    Player find = new Player();

    for (Player player : initializedPlayers) {
            if (login.getEmail().equals(player.getEmail()) && login.getPassword().equals(player.getPassword())) {
                loggedPlayer = player;

            return loggedPlayer;
            }
    }
    throw new AuthenticationException("Incorrect email and/or password");
}

@Override
public List<Bet> findAllBets() {
    return dds.findAllBetVariations();
}

@Override
public Wager createWager(Player player, Outcome outcome, BigDecimal amount) throws LowBalanceException {

    Wager wager = new Wager();
    wager.setOutcome(outcome);
    wager.setPlayer(player);
    wager.setCurrency(player.getCurrency());
    wager.setTimeStampCreated(LocalDateTime.now());
    wager.setAmount(amount);
    if(getLoggedPlayer().getBalance().compareTo(amount) < 0){
        throw new LowBalanceException(loggedPlayer.getBalance().toString(), loggedPlayer.getCurrency());
    }
    getLoggedPlayer().setBalance(getLoggedPlayer().getBalance().subtract(amount));
    if(wager.getOutcome().isWin()){
        wager.setWin(true);
    }
    wagerDatastore.add(wager);
    return wager;
}

@Override
public List<Wager> findAllWagers() {
    return wagerDatastore;
}

@Override
public void calculateResults() {
    BigDecimal counter = BigDecimal.valueOf(0);
    for(int i = 0; i < wagerDatastore.size(); i++){

        if(wagerDatastore.get(i).isWin()){

            counter = counter.add(wagerDatastore.get(i).getOutcome().getOdd().multiply(wagerDatastore.get(i).getAmount()));
            wagerDatastore.get(i).getPlayer().setBalance(counter);
        }

    }
}

public Player getLoggedPlayer() {
    return loggedPlayer;
}

public void setLoggedPlayer(Player loggedPlayer) {
    this.loggedPlayer = loggedPlayer;
}

}


Solution

  • Several issues got added together.

    1. DefaultSportsBettingService is an object under test, so you don't want to mock it.

    2. In your case NullPointerException comes from getLoggedPlayer as loggerPlayer has never been initialized.

    Several less important things that make testing terrible:

    1. LocalDateTime.now() is static, so you cannot test it was set correctly, so better to pass it as an external parameter (out of the scope of these answers)

    Approximate solution can look like this (cannot test it in IDE):

    @ExtendWith(MockitoExtension.class)
    class DefaultSportsBettingServiceTest {
    
        @Mock
        Player loggedPlayer
    
        @InjectMocks
        DefaultSportsBettingService service
    
        @Test
        public void testCreateWagerGetsCreated(){
            Mockito.when(loggedPlayer.getBalance()).thenReturn(BigDecimal.ZERO);
            Mockito.doNothing().when(loggedPlayer).setBalance(BigDecimal(15));
            Player player = Player(); // May require more params
            Outcome outcome = new Outcome(null,"description",BigDecimal.valueOf(2),false);
            BigDecimal bd = BigDecimal.valueOf(100);
            assertNotNull(service.createWager(player,outcome, bd));
        }
    
    }
    

    Basically, you mock loggedPlayer and rest pass as normal objects and expect that service.createWager works correctly. From here Mockito should suggest you if there other corrections required to testing code or test values.