Published on

Mocking Safety Net

Authors

When mocking with Mockito there is an option you can use to check you have not missed no assertions on a mock called verifyNoMoreInteractions. An approach I use is to make a helper function I can call to make sure I have not missed any mock dependencies in a test. This adds a nice safety net as you can call it at the end of all your assertions for a given test and it will break if you test an existing dependency's new method but forget to assert on it or miss asserting another mocked dependency.

The approach is as follows:

Define a Helper to Return the Service Under Test With Mocks Slotted In

If I for example have this service:

class PersonService(
    internal val personRepository: PersonRepository,
    internal val addressRepository: AddressRepository
){
    //...
    fun someAwesomeMethod(): String {
        // ...
    }
}

In the above I intentionally make the dependencies we wish to mock internal as this allows us to access the mocked dependency in the test later without anything fancy.

Then when I test it I define a helper function to return the service with the dependencies mocked:


//...

private fun personService(
    personRepository: PersonRepository = mock(), // this uses the Kotlin Mockito library to infer the mock type
    addressRepository: AddressRepository = mock()
): PersonService = PersonService(personRepository= personRepository, addressRepository= addressRepository)

The above refers to the Kotlin Mockito Library here

Define An Extension Function on The Service Under Test To Verify All Mocked Dependencies

I follow this up with an extension function in the test class to ensure all mock interactions have been accounted for:

private fun PersonService.verifyNoMoreInteractionsAtAll() = apply {
    verifyNoMoreInteractions(this.personRepository)
    verifyNoMoreInteractions(this.addressRepository)
 }

Use the Extension Function After All Other Assertions

Finally the last step is to just call personService.verifyNoMoreInteractionsAtAll() after all your test assertions.


val service = personService()

// ensure you have your mocks here
whenever(service.personRepository.someMethod(any(), any())).thenReturn("foo")
whenever(service.addressRepository.someOtherMethod(any(), any(), anyOrNull())).thenReturn("bar")


val result = service.someAwesomeMethod()

verify(service.personRepository, times(1)).someMethod(any(), any()))
// oops we did not verify the addressRepository method ^ ^

service.verifyNoMoreInteractionsAtAll() // this will error out now

This is great as it adds a safety net where if you forget to very/test some mocked dependency, the test will fail. In the above for example we did not verify the addressRepository we mocked so the last line would fail.

The biggest caveat with this approach is that we need to remember to add new dependencies to the extension function but this approach still adds a nice fairly simply safety net.

If you are doing this in Java you can easily make a normal function that takes in the PersonService and then has those verifies inside it.