Transcripted Summary

In addition to giving you the ability to create mocks, Mockito also allows you to create spies.

Spies are a little bit different from mocks. Recall when we were mocking, we really didn't have a check that existed. We only had the interface and so we were just simulating returns.

Mockito is doing a similar thing when you talk about spies, but we're actually using actual objects and intercepting those calls and we can actually validate and verify that certain objects were called with specific parameters. And so here the major difference is that these are real objects.

Let's set up a scenario where we can actually see this in action.


    /**

     * Customers should be able to receive notifications that a check has been processed.

     * Scenario:

     * 1. Given I enable E-mail notifications on the checking account

     * 2. And I have a valid check #4321 for amount $100.00

     * 3. When I process check #4321

     * 4. Then the transaction notification should be sent via e-mail

     * 5. And the account balance should be $150.00

     */

And so here, our scenario is going to be that customers should be able to receive notifications that a check has been processed.

So, on our checking account we can actually enable notifications and whenever a check is processed, a customer should get notification. In this case, via email that that check has been processed.

So, our set-up is given, I enable notifications on the checking account and let's use email notifications to be very specific here, and I have a valid check numbered 4321 for an amount of $100.

When I process check 4321, then the transaction notification should be sent via email, and the account balance should be $150.


And so now we have this scenario, and this is actually a great use of a spy, because typically an email notification service would probably cross transactional boundaries.

In other words, it would be in a different subsystem, and in this system, we can see that our notification service is indeed a separate package.

So, if we just wanted to focus on the BankService, but we wanted to make sure that it was connecting out to the notification service and sending the right parameters without necessarily using the full verification of what is happening on the notification service, we can do that using a spy.

And so here we can define our test to see exactly what this looks like, but we want to first create the spy.

@Spy
EmailService spiedEmailService = new EmailService();

/**

 * Customers should be able to receive notifications that a check has been processed.

 * Scenario:

 * 1. Given I enable E-mail notifications on the checking account

 * 2. And I have a valid check #4321 for amount $100.00

 * 3. When I process check #4321

 * 4. Then the transaction notification should be sent via e-mail

 * 5. And the account balance should be $150.00

 */

So similar to mocks, we can create the spy where we declare the email service. We'll call it our spiedEmailService, and this is new EmailService.

So here we see that we use the instantiation of an actual object. And just remember you may have to include these things in your imports if they're not already there. I've already set up a lot of that.

And so now for a test.


    @Test
    public void processingCheckWithNotificationsEnabled_DecreaseBalanceAndSendsMessage() {

        // Given

        checking.enableNotifications(spiedEmailService);

        when(mockCheck.getCheckNumber()).thenReturn(4321);

        when(mockCheck.getAmount()).thenReturn(100.0);

        // When

        checking.processCheck(mockCheck);

        // Then

        verify(spiedEmailService).notify(contains("Check #4321"), eq(checking.getOwner().getEmail()));

        assertEquals(checking.getBalance(), 150.00);

    }

}

Let's set that up , and we'll call this one processingCheckWithNotificationsEnabled_DecreaseBalanceAndSendsMessage.

So here we can implement our Given. The first thing to do is to make sure that we have enableNotifications on that particular spiedEmailService. So that's set up for that.

Then we need our Check, so we'll use a mock here. We have our check number and our amount. We'll actually use the check number in this because we'll see as we start to validate the parameters that are sent to the call to the spiedEmailService. So now we have that set up.

We can now processCheck and mockCheck.

Now comes our verification, and so first thing we're going to get out of the way, is just that we want to make sure that that balance is updated and that's equal to $150.

And now here is where we use Mockito's verify statement where the verify statement allows us to use spiedEmailService.

We want to check that in terms of that spiedEmailService that the notify function gets called, and the notify function actually takes two parameters, message and the recipient. What we can do here is use some advanced features of Mockito to actually verify that email message and that call contains certain information.

So, for example, we can say that it should contain the string of the check number — contains("Check #4321").

Now that second parameter is really the recipient, which should be the email of the owner of the account. So, we can say that that parameter should be equal to, in this we use the eq function — eq(checking.getOwner().getEmail()) — so that we can verify that that email belongs to the owner.

Here we can really see the power of a mock and also a spy because not only do we have the Check, but we are now able to look at the calls that are stretching across subsystems and to intercept those calls and make sure that we can verify that certain parameters are being met.



Resources