logo
Solutionunittest

Mockito no interactions

Views: 709 Created: 2019-06-14 Read time: 3 minutes
Post Preview
Tags:

1)     Preface

Sometimes it is not the specific action that was triggered on a mock that is the point of interest.

Sometimes we want to create a sort of a guard verification that during a particular scenario, no interactions happen for one or some of our mocks.

The three main situations would be:

      Single method not called (with specific parameters)

      No interactions happened at all for a mock

      After some point in the test, no more interactions happened for a mock

2)     Mockito no interactions: single shy method

There are situations that we want to be completely confident, that apart from the primary test verification, we would also like to make sure that some other dependency has not been touched.

SUT Code


public void levelUp(Hero hero){
    hero.setLevel(hero.getLevel() + 1);

    if(hero.getLevel() % 10 == 0){
        hero.addSpell(gameEngine.generateSpecialSpell());
    }

    if(hero.getLevel() % 5 == 0){
        hero.addMoney(gameEngine.generateBonusMoney());
    }

    if(hero.getActiveBuff() == null){
        hero.setActiveBuff(gameEngine.generateRandomBuff());
    }
}

In this SUT method we are:

      (L3):  Raising the hero's level by one.

      (L5-7):  If a hero's level is 10,20,30 etc. then he is awarded a special spell into his spellbook.

      (L9-11):  If the hero level is 5,10,15 etc. then he is awarded a bonus amount of money by the game.

      (L13-15):  If the hero does not have any active buffs during the level-up process then is given a random one by the game engine.

Now let's try to test a scenario where the hero will be levelling from 14 to 15:

Test Code


@InjectMocks
private GameController gameControllerSUT;

@Mock
private GameEngine gameEngineMock;

@Test
public void shouldLevelUpWithoutBonusSpell() throws Exception{
    // Arrange
    Hero hero = new Hero();
    hero.setLevel(14);

    // Act
    gameControllerSUT.levelUp(hero);

    // Assert
    verify(gameEngineMock).generateBonusMoney();
    verify(gameEngineMock).generateRandomBuff();

    verify(gameEngineMock, never()).generateSpecialSpell();
}

In this test method we are:

      (L11,12):  Preparing the hero details for this particular test scenario.

      (L15):  Triggering the level-up process.

      (L18,19):  Making sure that hero received his bonus money and random buff.

      (L21):  Here we are making sure that the game engine did not waste any time on generating and add a special spell for the hero.

3)     Mockito no interactions on a whole mocked object

On some occasions, we want to make sure that completely no interaction with a certain mock's interface:

SUT Code


public boolean watchAnOffer(Auction auction, Buyer buyer) throws WatchingException {
    if(buyer.getWatchedAuctions().size() > MAX_NUMBER_OF_WATCHED_AUCTIONS){
        throw new WatchingException("Max number of offers are already watched.");
    }

    for(Auction watchedAuction: buyer.getWatchedAuctions()) {
        if(watchedAuction.getId().equals(auctionId)){
            throw new WatchingException("Auction is already watched.");
        }
    }

	auction.addWatcher(buyer);

    mailService.sendWatchConfirmationToUser(auctionId, personId);

    if(buyer.isSendNotificationToFriends()){
        mailService.sendWatchConfirmationToUserFriends(auctionId, personId);
    }

    return true;
}

In this SUT we are:

      (L3-5):  Checking whether the user has exceeded the maximum number of watched auctions. Throw exceptions if true.

      (L7-11):  Checking whether the user is already watching the auction. Throw exception if true.

      (L13):  Add the buyer to the auction watchers list.

      (L13):  Send a notification mail to the buyer.

      (L17-19):  Send additional notification to buyer friends if configured.

Now we really would not like to send any emails to the buyer and especially his friends when something fails beforehand.

That is why we will test any exceptional scenario and make sure that the MailService has not been touched ever.

Test Code


@Test
public void shouldNotSendAnyMailsOnException() throws Exception{
    // Arrange
    Integer auctionToWatchId = Integer.valueOf(10);
    Auction auctionToWatch = new Auction(auctionToWatchId);
    Buyer buyerOne = createBuyerWithMaxNumberOfWatchedAuctions();
	Buyer buyerTwo = createBuyerWithWatchedAuction10();

    // Act
    try {
        basicAuctionServiceSUT.watchAnOffer(auctionToWatch, buyerTwo);
    }catch (WatchingException e){}


    try {
        basicAuctionServiceSUT.watchAnOffer(auctionToWatch, buyerTwo);
    }catch (WatchingException e){}

    // Assert
    verifyZeroInteractions(mailService);
}

 In this test code we are:

      (L7-8):  Creating buyers that will provide the method to throw an exception.

      (L12,17):  Calling the SUT with the set-up data. WatchingException is thrown each time.

      (L21):  Finally, we make sure that in both of the actions, none of the MailService methods has been called.

4)     Mockito no more interactions

Finally, we can exclude all the interactions except those included in one of our verify statements:

SUT Code


public class RemoteControlController {

	public void triggerGeneralCleaning(){
		robotCleanerDriver.loadGeneralProgram();
		robotCleanerDriver.startCleaning();

		if(robotCleanerDriver.isFull()){
			robotCleanerDriver.emptyBin();
		}
		if(!robotCleanerDriver.allRoomsAccessed()){
			auditService.reportNoAllRoomsCleaned(
					robotCleanerDriver.getLastCleanStatistics());
		}

		robotCleanerDriver.returnToDockingStation();
	}

In this SUT we are:

      (L3,4,14):  Triggering our remote control to start the general cleaning process and perform standard actions.

      (L9,12):  Special events may happen that is in addition to the standard actions.

We want to write a test that makes sure that when nothing special happens during the process, only the standard actions are triggered and nothing else.

Test Code


@Mock
private RobotCleanerDriver robotCleanerDriverMock;

@Mock
private AuditService auditServiceMock;

@InjectMocks
private RemoteControlController remoteControlControllerSUT;

@Test
public void shouldPerformCleaningWithoutInterruptions() throws Exception{
    // Arrange
    when(robotCleanerDriverMock.allRoomsAccessed()).thenReturn(true);
    when(robotCleanerDriverMock.isFull()).thenReturn(false);

    // Act
    remoteControlControllerSUT.triggerGeneralCleaning();

    // Assert
    verify(robotCleanerDriverMock).loadGeneralProgram();
    verify(robotCleanerDriverMock).startCleaning();
    verify(robotCleanerDriverMock).returnToDockingStation();
    verify(robotCleanerDriverMock).allRoomsAccessed();
    verify(robotCleanerDriverMock).isFull();

    verifyNoMoreInteractions(robotCleanerDriverMock, auditServiceMock);

}

In this test we are:

      (L14,15):  Preparing the robotCleanerDriverMock for a standard clean without any other problems.

      (L21-25):  Verifying the standard actions were invoked.

      (L27):  Verifying that no other steps were performed either on the AuditService nor RobotCleanerDriver.

5)     Conclusion

Summary

 

We have seen that Mockito provides us with a variety of ways to check that there were no interactions with certain mocks.

This is useful in those tests validating the more significant parts of our application. Right there and then we want to be absolutely sure that: all the values are correct, what did suppose to happen has happened, and, well that some parts of the code were simply left to themselves. At least they did not have any distractions!

 

Need more insight?
Repository
Repository
Glossary
Glossary
Tags:
Reference
You may also like:
mockito-verification
Mockito verification
spying-with-mockito
Spying with Mockito
mockito-argument-capturing
Mockito argument capturing
Comments
Be the first to comment.
Leave a comment