Excessive Specification of Test Doubles
May 20, 2020Previously 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.
If you and your team want to learn more about how to write maintainable unit tests and get the most out of TDD practices, make sure to have look at our trainings and workshops or check out the books section. Feel free to reach out at info. @ principal-it .be
Jan Van Ryswyck
Thank you for visiting my blog. I’m a professional software developer since Y2K. A blogger since Y2K+5. Provider of training and coaching in XP practices. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.
Comments
Writing Maintainable
Unit Tests
Watch The Videos
Latest articles
-
Contract Tests - Parameterised Test Cases
June 28, 2023
-
Contract Tests - Abstract Test Cases
April 12, 2023
-
Contract Tests
February 1, 2023
-
The Testing Quadrant
June 15, 2022
-
Tales Of TDD: The Big Refactoring
February 2, 2022
Tags
- .NET
- ALT.NET
- ASP.NET
- Agile
- Announcement
- Architecture
- Behavior-Driven Development
- C++
- CQRS
- Clojure
- CoffeeScript
- Community
- Concurrent Programming
- Conferences
- Continuous Integration
- Core Skills
- CouchDB
- Database
- Design Patterns
- Domain-Driven Design
- Event Sourcing
- F#
- Fluent Interfaces
- Functional Programming
- Hacking
- Humor
- Java
- JavaScript
- Linux
- Microsoft
- NHibernate
- NoSQL
- Node.js
- Object-Relational Mapping
- Open Source
- Reading
- Ruby
- Software Design
- SourceControl
- Test-Driven Development
- Testing
- Tools
- Visual Studio
- Web
- Windows
Disclaimer
The opinions expressed on this blog are my own personal opinions. These do NOT represent anyone else’s view on the world in any way whatsoever.
About
Thank you for visiting my website. I’m a professional software developer since Y2K. A blogger since Y2K+5. Author of Writing Maintainable Unit Tests. Provider of training and coaching in XP practices. Curator of the Awesome Talks list. Thinking and learning about all kinds of technologies since forever.
Latest articles
Contract Tests - Parameterised Test Cases
Contract Tests - Abstract Test Cases
Contract Tests
The Testing Quadrant
Contact information
(+32) 496 38 00 82
info @ principal-it .be