2025 Public Training Schedule
January 14 – 17, 2025 – Agile Analysis and Design Patterns – Half-Day Sessions
(c) 2024 To Be Agile
I want to conclude this series of Seven Strategies for Great Acceptance Tests with the advice to make each test unique. I know this is easier said than done but it does get to the very core of what quality software development it’s all about.
When our unit tests test units of behavior, then every unit test is also an acceptance test. We are testing behaviors in the system and not how we implement those behaviors. This is the key to making our tests support refactoring of our code and helps us drop the cost of extending the software that we built.
The number one reason that I see teams fail when practicing both acceptance test-driven development and test-driven development is they think of these activities as testing activities and write far too many tests than is needed to specify the behavior they want to create. They’re using those tests as a form of quality assurance rather than as a form of specifying behaviors.
The tests that we write when we do acceptance test-driven development represent ideal examples of the kind of scenarios we expect in the system. Acceptance tests are not meant to fully describe the system but rather to just call out the main acceptance criteria for a feature. We want to do this in the most implementation-independent way that we can. This allows us the freedom to change the feature’s implementation details laster without breaking the acceptance test.
One of the main reasons that builds are slow is having redundant tests where we’re testing the same things over and over again. When constructing a test suite, we have to be sensitive to this issue and test each behavior once in the system. This is one of the hardest things I find for developers to practice. More tests are not always better.
I use tests as a way of articulating the behavior in a system. This is true for both acceptance tests at a high level and for unit tests at a much lower level when I’m implementing the behaviors of the system.
If I’m writing a blog post or a program specification I wouldn’t randomly repeat myself. If I’m writing a blog post or a program specification I wouldn’t randomly repeat myself (sorry). Why would I repeat myself in my test suite?
So what I’m saying is that if we think of our acceptance tests as specifying the behaviors of a system at a high level then we get a good grasp of how they should be written and what their benefits are.
Every test should validate a single intention in the system. That intention or behavior should exist in only one place in the system and therefore there should be only one test that exercises and validates that behavior. When I build a system using acceptance test-driven development and test-driven development, I find that I have far fewer redundant tests and consequently far fewer redundant implementations in my system. This means that I’m being more productive.
Note: This blog post is based on a section in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software.
Previous Post: « Split Behaviors on Acceptance Criteria
Next Post: Why Practice 7: Specify Behaviors with Tests »