Previously we’ve discussed the different kinds of test doubles. By using test doubles in our solitary tests, we also introduce more coupling between the test code and the implementation. However, the good news is that there are also some things we can do in order to somewhat reduce this coupling.
A very important item of caution is to avoid excessive specification of method behaviour. Let’s have a look at an example.
var approverId = new Guid("224FE5B8-EDBB-4F8B-8654-715C1C294CFD");
HeadOfDepartment headOfDepartment = Example.HeadOfDepartment().WithId(approverId);
var approverRepositoryMock = Substitute.For<IApproverRepository>();
approverRepositoryMock.Received().Get(approverId).Returns(headOfDepartment);
Here we create a mock for the IApproverRepository interface, created by the NSubstitute framework. When the subject under test makes a call to the Get method using a specific identifier, then an instance of the HeadOfDepartment is returned. However, that’s not the only thing that happens. The mock also verifies whether the subject under test actually makes a call to the Get method. If not, then the test will fail. The approach taken in this example illustrates the concept of excessive specification.
With behaviour verification, the degree of coupling between the test code and the implementation depends on the type of test double being used. Using a Dummy or a Stub results in the least amount of coupling, while using a Spy or a Mock induces the highest amount of coupling.
For indirect inputs, as is the case in our example, we should avoid using mocks at all times. We should use stubs instead. Let’s have a look at an improved version of the previous example.
HeadOfDepartment headOfDepartment = Example.HeadOfDepartment()
.WithId(new Guid("224FE5B8-EDBB-4F8B-8654-715C1C294CFD"));
var approverRepositoryStub = Substitute.For<IApproverRepository>();
approverRepositoryStub.Get(Guid.Empty).ReturnsForAnyArgs(headOfDepartment);
Here we no longer verify whether the Get method is being called. We only state that whenever the Get method of the ApproverRepository is called, no matter what the identifier might be, just return the specified HeadOfDepartment object. So we basically switched from using a mock to using a stub.
For solitary tests that make use of test doubles, prefer the use of stubs over spies and mocks. Generalisation of stub method arguments make solitary tests more resilient to changes. I always try to use the most generic argument matcher that is feasible for a particular scenario.
Obviously, we sometimes need to use spies or mocks. For those cases, it’s very important to restrict their use to only verifying indirect outputs. We should only specify what should happen and nothing more.
The following piece of test code is an example where the use of a mock is warranted.
[Observation]
public void Then_the_approved_expense_sheet_should_be_saved()
{
_expenseSheetRepositoryMock.Received().Save(_expenseSheet);
}
When observing solitary tests that make use of test doubles in a decent code base, you might see that the usage ratio of dummies and/or stubs is significantly higher than that of spies and/or mocks. This should give us a decent idea about the usage of the different types of test doubles.
Top comments (0)