2024 Public Training Schedule
November 18 – 21, 2024 – Agile Analysis and Design Patterns
Half-Day Sessions
December 9 – 12, 2024 – Agile Analysis and Design Patterns
Half-Day Sessions
(c) 2024 To Be Agile
Unit testing frameworks are simple but I find them highly valuable. They contain a collection of assertions that I can use to validate a range of values and behaviors in the code that I’m building. I use assertions to verify that values are within bounds, exceptions are called when expected and not called when unexpected, and that logic and behavior is correctly implemented. This covers a great deal of the things that I want to test in my system but it doesn’t cover everything.
The thing that unit testing frameworks cannot really test easily is workflows. We often find that in complex software we are doing a series of steps, some of which are predicated upon various conditions, and these kinds of complex workflows are not well suited for testing with assertion frameworks. Fortunately, there are tools that are well-suited for that, namely test doubles or mocks.
Technically speaking, mocks are a special kind of test double but I often use the term mock generically to mean any kind of test double. I find just writing a simple mock that either is a subclass of the class I want to mock or implements the same interface, that I can avoid using a mocking framework and this can drop the overhead of my tests significantly.
Let’s say I have a collection a validator’s that I want to apply to a password. I want to ensure that all the validators are called when I call them out of a loop when I go through the collection of validators and call each one. In such a scenario, I might have a mock validator that I put at the end of the collection that merely keeps track of whether it was invoked or not, and then later my test code will interrogate that mock and see whether it was called. If it was then I know that all of the previous validators were also called. This is one way to use a mock to test workflows.
Mocking frameworks have become quite sophisticated and it seems like every week a new one comes out. There are many different kinds of mocking frameworks or test double frameworks that you can draw on but I very much prefer to roll my own whenever I can.
If I have a DAO that I’m using to access a database, for example, then I can mock the DAO by subclassing it and overriding the calls that access the database to return hard-coded values more quickly than real database accesses so the test runs faster. Alternatively, my production code can access the DAO through a common interface that allows me to mock the interface and take the database out of the equation of testing completely.
These kinds of techniques are super simple but often I find are sufficient for the simple tests that I need to write when I’m doing test-first development. Using a combination of mocks and an assertion testing framework, I find that I can write all the tests that I need to validate that all the behaviors in a system work as expected.
Note: This blog post is based on one of the “Seven Strategies…” sections in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Previous Post: « Test Behaviors, Not Implementations
Next Post: Avoid Over-Specifying Tests »