logo
Solutionunittest

Mockito settings

Views: 627 Created: 2019-02-07 Read time: 3 minutes
Post Preview
Tags:

1)     Preface

If you are feeling like trying some Hipster features of Mockito then you definitely you need to check out the MockitoSettings interface.

You can experiment with stuff like additional logging, default answers, invoking constructors and much more. Let's dive right into it.

 

Tip
Tip

 

2)     Mockito MockSettings: where did the default constructor go?

In general, we should be mocking interfaces, not concrete classes. Sometimes though we need to use a spy and more particularly, on an abstract class. We want to verify some of its behaviours, and sometimes we want to invoke its real implementation. But most important we would like to choose the constructor that it will be created with:

SUT Code


public abstract class CarPrototype implements Car{

	private Color color;
	private Integer numberOfDoors;
	private Engine engine;

	public CarPrototype(Color color){...}

	public CarPrototype(Color color, Engine engine){...}

	public CarPrototype(Color color, Engine engine, Integer numberOfDoors){...}

	...

}

 

By default, Mockito does not use any constructor while creating a mocked object. That means for the purpose of our test some of the instance fields that would be used, will not get instantiated.

Let's put a remedy to that:

Test Code


Color blueColor = new BlueColor();
Engine v8Engine = new V8Engine();

CarPrototype coolPrototype = mock(CarPrototype.class,
		withSettings().useConstructor(blueColor, v8Engine)
					  .name("coolBluesy"));

 

Note
Note

 

3)     Mockito MockSettings: default answer

If you take a closer look at the example above you will notice that something is not quite right. We did not create an actual partial mock as all the method will return default Mockito values.

Let's do something about that:

Test Code


Color blueColor = new BlueColor();
Engine v8Engine = new V8Engine();

CarPrototype coolPrototype = mock(CarPrototype.class,
		withSettings().useConstructor(blueColor, v8Engine)
					  .defaultAnswer(CALLS_REAL_METHODS)
					  .name("coolBluesy"));

Now the mocked instance will use real implementation by default.

 

Tip
Tip

4)     Mockito MockSettings: stub but don't record

Sometimes stubbed classes are invoked many, many times during the life of a test. Most notably this is a scenario that happens in IT. If our only aim is to stub the invocations without the need to verify the recorded behaviour then MockitoSettings has got an answer to that:

Test Code


VeryOftenUsedCoreRepository veryOftenUsedCoreRepositoryStub
	= mock(VeryOftenUsedCoreRepository.class, withSettings().stubOnly());

Now Mockito will not record any activity to that stub, therefore saving memory and precious time. Time is crucial, we cannot have a test suite that takes ages to finish as no one will run it!

5)     Mockito MockSettings: additional logging

Finally, we end up with something that can be a beneficial tool when there a lot of mock / stubs interactions in our tests and we get a bit lost in that maze.

Here we will be trying to make an order 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);
}

 

Test Code


@Test
public void shouldBookATable() throws Exception{
    // Arrange
    Table tableToBook = new Table();
    Customer customer = new Customer();
    RestaurantSystem restaurantSystemMock = mock(RestaurantSystem.class
            , withSettings().verboseLogging());
    Manager managerMock = mock(Manager.class
            , withSettings().verboseLogging());

    restaurantControllerSUT.setRestaurantSystem(restaurantSystemMock);
    restaurantControllerSUT.setManager(managerMock);

    doReturn(tableToBook).when(managerMock).findFreeTable(restaurantSystemMock);

    // Act
    restaurantControllerSUT.bookTable(customer);

    // Assert
    verify(restaurantSystemMock).bookTable(tableToBook);
    verify(managerMock, times(1)).confirmBooking(customer);
}

 

With additional logging turned on this is what we might get:


############ Logging method invocation #1 on mock/spy ########
manager.findFreeTable(
    Mock for RestaurantSystem, hashCode: 1242027525
);
   invoked: -> at com.sourceartists.restaurant.controller.RestaurantControllerTest.shouldBookATable
   has returned: "null"

############ Logging method invocation #2 on mock/spy ########
manager.answerCall(
    com.sourceartists.restaurant.model.Customer@68746f22
);
   invoked: -> at com.sourceartists.restaurant.controller.RestaurantController.bookTable
   has returned: "null"

############ Logging method invocation #3 on mock/spy ########
   stubbed: -> at com.sourceartists.restaurant.controller.RestaurantControllerTest.shouldBookATable
############ Logging method invocation #3 on mock/spy ########
restaurantSystem.toString();
   invoked: -> at java.lang.String.valueOf(String.java:2994)
   has returned: "Mock for RestaurantSystem, hashCode: 1242027525" (java.lang.String)

############ Logging method invocation #4 on mock/spy ########
restaurantSystem.toString();
   invoked: -> at java.lang.String.valueOf(String.java:2994)
   has returned: "Mock for RestaurantSystem, hashCode: 1242027525" (java.lang.String)

manager.findFreeTable(
    Mock for RestaurantSystem, hashCode: 1242027525
);
   invoked: -> at com.sourceartists.restaurant.controller.RestaurantController.bookTable
   has returned: "com.sourceartists.restaurant.model.Table@4b86805d"

############ Logging method invocation #5 on mock/spy ########
restaurantSystem.bookTable(
    com.sourceartists.restaurant.model.Table@4b86805d
);
   invoked: -> at com.sourceartists.restaurant.controller.RestaurantController.bookTable
   has returned: "null"

############ Logging method invocation #4 on mock/spy ########
manager.confirmBooking(
    com.sourceartists.restaurant.model.Customer@68746f22
);
   invoked: -> at com.sourceartists.restaurant.controller.RestaurantController.bookTable
   has returned: "null"


############ Logging method invocation #5 on mock/spy ########
manager.confirmBooking(
    com.sourceartists.restaurant.model.Customer@68746f22
);
   invoked: -> at com.sourceartists.restaurant.controller.RestaurantControllerTest.shouldBookATable
   has returned: "null"

 

Tip
Tip

 

6)     Conclusion

 

Summary

 

As we have seen, MockitoSettings allows for that extra precise configuration. So, if you are up for that hipster tune-up, grab these tools and get to work.

Need more insight?
Repository
Repository
Glossary
Glossary
Tags:
Reference
You may also like:
bypass-manual-testing-and-save-tons-fo-time
Bypass manual testing and save tons of time
mockito-most-common-exceptions
Mockito most common exceptions
mockito-stubbing
Mockito stubbing
Comments
Be the first to comment.
Leave a comment