Verifying the order of interactions

In this recipe, we will verify that a set of methods get executed in the specified order.

Getting ready

For this recipe, our system under test will be TaxUpdator which is a simplified version of a facade that calls the TaxService methods (let's assume that it is a web service) to update tax-related data and perform a series of tax transfers. Let's assume that this web service is a legacy, a badly-written system, and we have to synchronously call it in a precisely defined sequence.

Let's take a look at the implementation of the TaxUpdator class:

public class TaxUpdator {

    public static final int TAX_FACTOR = 100;

    private final TaxService taxService;

    public TaxUpdator(TaxService taxService) {
        this.taxService = taxService;
    }

    public void transferTaxFor(Person person) {
        taxService.updateTaxFactor(person, calculateTaxFactor(1));
        taxService.transferTaxFor(person);
        taxService.transferTaxFor(person);
        taxService.updateTaxFactor(person, calculateTaxFactor(2));
        taxService.transferTaxFor(person);
    }

    private double calculateTaxFactor(double ratio) {
        return TAX_FACTOR * ratio;
    }

}

How to do it...

To verify whether the mock's method execution took place in a specified order, perform the following steps:

  1. Start the verification in order using InOrder inOrder = Mockito.inOrder(mock1, mock2, … , mockn);, where mock1, mock2, and mockn are the objects that might be used in the verification process.
  2. Then, you can call either of the following presented methods in a specified sequence to verify that their execution took place in the specified order:
    inOrder.verify(mock).method(...);
    inOrder.verify(mock, verificationMode).method(...);
    inOrder.verifyNoMoreInteractions()

Let's check the JUnit test that verifies whether the web service's method has been called at most twice (see Chapter 1, Getting Started with Mockito, for the TestNG configuration):

@RunWith(MockitoJUnitRunner.class)
public class TaxUpdaterTest {

    @Mock TaxService taxService;

    @InjectMocks TaxUpdater systemUnderTest;

    @Test
    public void should_update_tax_factor_and_transfer_tax_in_specified_order() {
        // given
        Person person = new Person();

        // when
        systemUnderTest.transferTaxFor(person);

        // then
        InOrder inOrder = Mockito.inOrder(taxService);
        inOrder.verify(taxService).updateTaxFactor(eq(person), anyDouble());
        inOrder.verify(taxService, times(2)).transferTaxFor(person);
        inOrder.verify(taxService).updateTaxFactor(eq(person), anyDouble());
        inOrder.verify(taxService).transferTaxFor(person);
    }
}

How it works...

When you create the InOrder object and define the desired order of execution, Mockito stores the expected order and then verifies it against the actual execution. During the iteration over the actual method invocations, depending on the passed verification mode (times(…), atLeast(…), and so on), Mockito marks either a single or multiple actual method executions as verified.

Let's try to depict this scenario using our test example. Having the inOrder.verify(taxService, times(2)).transferTaxFor(person); verification in order means that we are asking Mockito to mark two subsequent invocations of the transferTaxFor(…) method as verified and throw an exception if there were no such two subsequent calls.

The times(…) method returns a verification mode that is not greedy, which means that it will verify subsequent calls only. Take a look at the following code:

inOrder.verify(taxService, times(2)).transferTaxFor(person);
inOrder.verify(taxService).updateTaxFactor(eq(person), anyDouble());
inOrder.verify(taxService).transferTaxFor(person);

When Mockito goes past the first line, it will mark only two methods of transferTaxFor(…) as verified. If there are any other transferTaxFor(…) methods, Mockito will not mark them as verified. This will happen in the third line, where an additional verification takes place.

There is an interesting case of the calls(…) method that behaves in a different manner from the analogous times(…) and atLeast(…) methods that return VerificationMode. Let's have a look at the examples:

  • times(2): This method verifies that a method was executed exactly two times (it will fail if a method was invoked once or, for example, three times).
  • atLeast(2): This method verifies that the method was executed at least twice (it will fail if a method was invoked once. It marks all the subsequent method executions as verified).
  • calls(2): This method allows a non-greedy verification (check the There's more... section of this recipe for more information). If a method is executed three times, then calls(2) will not fail, unlike the analogous times(3). Also, it will not mark the third invocation as verified, unlike atLeast(2).

There's more...

As stated in the previous section, there are verification modes that are greedy; they will mark all the matching method executions as verified.

Let's imagine the following scenario (the test is based on the previous example):

@Test
    public void should_fail_at_updating_second_tax_factor_in_specified_order_due_to_greedy_at_least() {
        // given
        Person person = new Person();

        // when
        systemUnderTest.transferTaxFor(person);

        // then
        InOrder inOrder = Mockito.inOrder(taxService);
        inOrder.verify(taxService).updateTaxFactor(eq(person), anyDouble());
        inOrder.verify(taxService, atLeastOnce()).transferTaxFor(person);
        inOrder.verify(taxService).updateTaxFactor(eq(person), anyDouble());
        inOrder.verify(taxService).transferTaxFor(person);
    }

As you can see, the only difference between the tests is the following line (the difference is that we had times(2) and now we have atLeastOnce()):

inOrder.verify(taxService, atLeastOnce()).transferTaxFor(person);

This test won't succeed, which can seem very odd at first glance.

Let's have a look at the execution sequence of the system under the test's method in which we have all the mocked object's method executions:

taxService.updateTaxFactor(person, calculateTaxFactor(1));
taxService.transferTaxFor(person);
taxService.transferTaxFor(person);
taxService.updateTaxFactor(person, calculateTaxFactor(2));
taxService.transferTaxFor(person);

You may think that when we provide the atLeastOnce() method, Mockito will mark all the subsequent executions of the transferTaxFor method (in our case, there are two subsequent executions: lines two and three of the snippet) and then, the next verification step will be of the updateTaxFactor method, in line four of the snippet.

Since atLeastOnce() is greedy (atLeast() is always greedy in the InOrder verification), the following tasks take place:

  1. When the first transferTaxFor method is verified against the AtLeast verification mode, it marks all three transferTaxFor methods as verified (lines two, three, and five).
  2. Then, it starts the next step of verification after the last line (after line five) of our snippet (moving over line four). In our test code, the next step of verification is inOrder.verify(taxService).updateTaxFactor(eq(person), anyDouble()).
  3. Bear in mind that due to the greedy nature of the AtLeast verification mode, we moved to the last execution of the transferTaxFor method.
  4. Now, we need to execute the updateTaxFactor method.
  5. We will get a Mockito VerificationInOrderFailure exception since there is no such method. The message will look more or less like the one shown as follows:
    Wanted but not invoked:
    taxService.updateTaxFactor(
        Person@183b1e8b,
        <any>
    );
    -> at ExplainingTheGreedyAlgorithm.should_fail_at_updating_second_tax_factor_in_specified_order_due_to_greedy_at_least(ExplainingTheGreedyAlgorithm.java:56)
    Wanted anywhere AFTER following interaction:
    taxService.transferTaxFor(
        Person@183b1e8b
    );

Note

If you are only interested in the fact that a given method gets executed in a precise order and you don't care about the rest, you just have to explicitly define only those interactions that you are interested in. In other words, you must use the following methods:

taxService.transferTaxFor(person);
taxService.updateTaxFactor(person, taxFactor);
taxService.transferTaxFor(person);

If you are only interested in the fact that the transferTaxFor methods get executed one after another (ignore the updateTaxFactor method), you would just have to write the following code:

InOrder inOrder = Mockito.inOrder(taxService);
inOrder.verify(taxService).transferTaxFor(person);
inOrder.verify(taxService).transferTaxFor(person);

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.212.124