logo
Solutionunittest

Mockito wildcards

Views: 963 Created: 2019-04-26 Read time: 4 minutes
Post Preview
Tags:

1)     Preface

There are situations when we are not exactly sure what object will be passed to one of our mocks. Sometimes we simply do not care. Mockito gives us the ability to use wildcards in a form ArgumentMatchers whether we are stubbing or verifying particular behaviour. Let us dive right into it.

 

2)     Mockito wildcards: accepting primitive types

Let us try to go through a basic example where we will be trying to open a restaurant. The code looks something like this:

 

SUT Code


public void openRestaurant(DayOfWeek dayOfWeek) throws WeAreClosedTodayException {
    if(dayOfWeek.equals(DayOfWeek.SUNDAY)){
        throw new WeAreClosedTodayException();
    }

    Integer hourOfOpening = restaurantSystem.getHourOfOpening(dayOfWeek);
    Integer minuteOfOpening = restaurantSystem.getMinuteOfOpening(dayOfWeek);

    boolean shouldOpen = manager.shouldOpenRestaurant(hourOfOpening, minuteOfOpening);

    if(shouldOpen){
        manager.open(restaurantSystem);
    }
}

In this SUT method, we are:

      (L3):  We are only opened MON-SAT. On Sunday cook your own food.

      (L7,8):  Based on the dayOfWeek we are pulling the opening time.

      (L10):  If the current time is just right, we open our restaurant.

Now let's test that on any day between Monday and Saturday no exception is thrown and the Manager can open the restaurant:

 

Test Code


@InjectMocks private RestaurantController restaurantControllerSUT;
@Mock private RestaurantSystem restaurantSystemMock;
@Mock private Manager managerMock;

@ParameterizedTest
@EnumSource(value = DayOfWeek.class, mode = EXCLUDE, names = {"SUNDAY"})
public void shouldOpenRestaurant(DayOfWeek dayOfWeek) throws Exception{
    // Arrange
    when(managerMock.shouldOpenRestaurant(anyInt(), anyInt())).thenReturn(true);

    // Act
    restaurantControllerSUT.openRestaurant(dayOfWeek);

    // Assert
    verify(managerMock).open(restaurantSystemMock);
}

In this test method, we are:

      (L6,7):  We will be invoking the same test for Monday to Saturday days of the week.

      (L11):  No matter what is the time and what time we are supposed to open, for the purpose of this test, we just open if dayOfWeek is right.

      (L17):  Make sure that the restaurant has been opened.

 

Tip
Tip

 

3)     Mockito any(): object wildcards

A lot of times, we will not be dealing with purely primitive types. Java is object-oriented, so there will be a lot of objects floating around. Now we will go through a case where we do not care what specific object has been passed, as long as it is of the proper type. This time we will be trying to book a table at our favourite restaurant:

 

SUT Code


public void bookTable(Customer customer){
    manager.answerCall(customer);

    Table table = manager.findFreeTable(restaurantSystem);

    if(table == null){
        restaurantSystem.notifyCustomerWhenTablesAvailable(customer);
        manager.appologiseAndConfirmNotification(customer);

        return;
    }

    restaurantSystem.bookTable(table);
    manager.confirmBooking(customer);
}

In this SUT method, we are:

      (L3):  Answering a call from the customer.

      (L5):  Our Manager queries the RestaurantSystem for a free table.

      (L14,15):  If a table has been found, we book it, and notify the customer.

Now let us try to write a test for the happy path here. Also, we do not care about the exact table that has been booked. We just want a table to get booked:

 

Test Code


@InjectMocks private RestaurantController restaurantControllerSUT;
@Mock private RestaurantSystem restaurantSystemMock;
@Mock private Manager managerMock;

@Test
public void shouldBookATable() throws Exception{
    // Arrange
    Customer customer = new Customer();

    doReturn(new Table()).when(managerMock).findFreeTable(restaurantSystemMock);

    // Act
    restaurantControllerSUT.bookTable(customer);

    // Assert
    verify(restaurantSystemMock).bookTable(any(Table.class));
    verify(managerMock).confirmBooking(customer);
}

In this test method, we are:

      (L11):  Making sure that there is a free Table available.

      (L17):  Verifying that a Table has been booked. Any table.

 

Tip
Tip

 

4)     Mockito var-args wildcards

Rarely but sometimes we need to deal with a var-args method. It may happen though, and Mockito has an answer to that as well. Let's this time, try to prepare a stew in our restaurant. Our master chef is already sharpening the knives.

 

SUT Code


public Meal prepareMeal(Order order, Chef chef) throws WeDontServeThisHereException{
    Meal meal = null;

    if(order.isSoup()){
        Ingredient[] ingredients = chef.prepareIngredients(order);

        meal = chef.cook(MealType.SOUP, ingredients);
    }else if(order.isStew()){
        Meat meat = chef.prepareMeat(order);
        VegetableMix vegetableMix = chef.prepareVegies(order);
        Sauce sauce = chef.prepareSauce(order);

        meal = chef.cook(MealType.STEW, meat, vegetableMix, sauce);
    }else{
        throw new WeDontServeThisHereException();
    }

    return meal;
}

In this SUT method, we are:

      (L5,9):  Our MasterChef specializes in soups and stews. He does not care about anything else.

      (L8,14):  The MasterChef.cook() method accepts a MealType and a set of Ingredients. Just based on that he knows what to do. He is a master in the end.

Let us write a test where we want to make sure that based on the Order our MasterChef prepares a yummy stew:

 

Test Code


@Mock private MasterChef masterChefMock;
@InjectMocks private RestaurantController restaurantControllerSUT;

@Test
public void shouldPrepareStew() throws Exception {
    // Arrange
    Meal meal = new Meal();
    Order order = new Order(MealType.STEW);

    when(masterChefMock.cook(eq(MealType.STEW), any())).thenReturn(meal);

    // Act
    Meal preparedMeal = restaurantControllerSUT.prepareMeal(order, masterChefMock);

    // Assert
    assertThat(preparedMeal).isSameAs(meal);
}

In this test method, we are:

      (L9):  Prepare an Order for a stew.

      (L11):  We make sure that when our MasterChef is given a stew to prepare with a bunch of Ingredients he will provide us with a great Meal. Watch out for that extra eq() wrapper around the MealType.STEW. This has to be used when we are mixing wildcards with actual values.

 

Note
Note

 

5)     Mockito ArgumentMatcher: create custom wildcards

If the standard ArgumentMatchers are not enough for you and you need that detailed insight of the passed objects state, then Mockito has it all figured out for you.

Let us go through a processBill method. The customer has to pay eventually.

 

SUT Code


public Bill processBill(Customer customer, Waiter waiter, Order order){
    Bill bill = waiter.prepareBillForOrder(order);
    Iterator<CreditCard> creditCards = customer.prepareToPay();

    while(!bill.paid()){
        if(creditCards.hasNext()){
            CreditCard creditCard = creditCards.next();

            boolean transactionSuccessful = waiter.performTransaction(bill, creditCard);

            if(transactionSuccessful){
                bill.setPaid(true);
                break;
            }else{
                waiter.informTransactionNotAccepted(customer, bill);
            }
        }else{
            throw new RuntimeException("Customer cannot pay");
        }
    }

    return bill;
}

In this SUT method, we are:

      (L6):  Until the Bill is paid the Customer has to try to pay with any CreditCard he has.

      (L16):  When the transaction is not successful, the Waiter has to inform the Customer and hand over the bill again.

Now we would like to test that when a transaction fails, and the Waiter passed the Bill again to the customer, it remained as unpaid. Otherwise, the Customer will have the right to go out without paying.

 

Test Code


@Test
public void shouldInformCustomerWhenTransactionDeclined() throws Exception{
    // Arrange
    Bill bill = new Bill();
    Bill billPassedToCustomerAfterDecline = new Bill();

    Order orderDummy = new Order();
    Waiter waiterMock = mock(Waiter.class);
    Customer customer = new Customer();
    customer.setCreditCards(Arrays.asList(new CreditCard[]{
            new CreditCard(1),new CreditCard(2)}));

    when(waiterMock.prepareBillForOrder(orderDummy)).thenReturn(bill);
    when(waiterMock.performTransaction(eq(bill), any(CreditCard.class)))
            .thenReturn(false)
            .thenReturn(true);

    doNothing().when(waiterMock).informTransactionNotAccepted(
            eq(customer), argThat((billPassed) -> {
        billPassedToCustomerAfterDecline.setPaid(billPassed.paid());

        return true;
    }));

    // Act
    Bill resultBill = restaurantControllerSUT.processBill(customer, waiterMock, orderDummy);

    // Assert
    assertThat(billPassedToCustomerAfterDecline.paid()).isFalse();
}

In this test method, we are:

      (L15):  Making sure that the Customers first payment has not been successful.

      (L19):  Saving the state of the Bill as it was passed to the waiterMock.informTransactionNotAccepted

      (L30):  Making sure that a non-paid Bill has been given to the customer.

 

There is an alternative for customer ArgumentMatchers called ArgumentCaptor. Check out my post dedicated entirely to this feature.

Reference
Reference
unittest
Mockito argument capturing

 

6)     Conclusion

 

Summary

 

We have seen examples all flavours of ArgumentMatchers provided to us by Mockito. Thanks to them, we can allow for any kind of input as long as it meets the type requirement. Having that said, keep in mind not to use the super-generic any() or anyObject() unless its really necessary. Let in any kind of sheep but never let in a wolf in sheep's skin.

Need more insight?
Repository
Repository
Glossary
Glossary
Tags:
Reference
You may also like:
test-method-naming
Test method naming
mockito-argument-capturing
Mockito argument capturing
mockito-stubbing
Mockito stubbing
Comments
Be the first to comment.
Leave a comment