This series of posts describes the process I use to decide which tests to write, and how to write them – regarding where if the code already exists or not. This approach works for both TDD and test-after, but it works well where most of the systems are – legacy mode. The process is about understanding the environment and fitting in the tests.
The process includes these steps:
- Understanding the problem
- Designing a solution
- Test categorization
- Testability design consideration
- Design constraints
- Analyze existing code
- Identify dependencies
- Define which code goes where
- Actually writing tests
- Re check UT / AT assumptions and coverage.In the series of posts, I’ll go into each step in details.
Step 1: Understanding the problem.
The problem we’re trying to solve is a set of use cases, or user stories, or any name for something that gives someone or something value. Even if this is a story that was sliced down pretty slim, there can be several test cases for it. Although it’s possible, these are not necessarily unit tests – they are mostly system use cases. They include all kinds of happy paths, validations, error handling, and others that the complete solution has to support. I usually use a mind map to draw these, and if possible have someone review it. Better yet, collaborate on building the map.
Step 2: Designing a solution.
This is a step that is needed if we haven’t written the code yet. But even if the feature is already done, or the bug is already fixed, it is still valuable to draw the existing design.
In TDD we’re told that the design will emerge from the tests. This is true, but your mileage may vary: Several designs can solve the problem, but and you may not choose the best. Thinking upfront can help avoid unnecessary waste.
So, even with TDD, define the main blocks of the system you’re going to write. The design helps identify the system boundaries, and where interaction with other system occur. This is a high-level design, and it may not be a final one. It should be enough to see if our design covers the test cases we’ve identified in step 1. It should be detailed enough to start writing the first tests.
If you’re just adding tests to an existing design, you’ll need that design to see if the test cases that you want to add are feasible, and where to test what. You’ll also identify where things should be mocked, or if you need to refactor in order to introduce seams.
We’ll continue the series next time.