Session

My 30-ish Laws of Test Driven Development

About 15 years ago, I got inspired by the practice of Test Driven Development. Now, with many years of great and not-so-great experiences practicing Test Driven Development, I thought it is the time to capture my own “laws”. The term "law" is obviously an exaggeration and "principles or heuristics" cover my intend much better. Either way, I want to talk about the scope of testing, using the observable behavior, naming conventions, state-based vs interaction-based testing, the impact of DRY, patterns to build useful test objects and some of the libraries and tools I use. In short, everything I try to follow every day I write, review or maintain code.

Design for testability
1. Start with the class design before you write your first tests
2. Then use the tests to drive the design further
3. Use functional boundaries
4. Things in adjacent folders usually mean they are separate boundaries
5. It's fine to inject concrete classes inside boundaries
6. Use the DIP to decouple code and make it more testable
7. Apply DRY within those boundaries
8. Don't bother defining the difference between "unit" and "integration". Use "appropriate-sized tests"

Scoping
1. Align your test scope with those internal boundaries…
2. But it's fine to test smaller sometimes
3. Test things that are designed for reusability separately.
4. Don't test implementation details separately, test them as part of the reusable scope
5. Test the real surface (HTTP APIs, not using the database)
https://www.continuousimprover.com/2023/03/test-http-contracts.html
6. It's fine to include the database in tests
https://www.continuousimprover.com/2023/03/docker-in-tests.html
7. Use the right style for the right type of tests (AAA vs BDD-style)

Test design guidelines
1. Treat tests as first-class citizens of your code base
2. Allow developers to use your tests as documentation
3. Use mocking frameworks between boundaries, but not internally
4. Don't return mocks from mocks
5. Make sure the test shows the arrange, act and assert explicitly
6. Hide things that are not important and show things like routes, the test data that are important to understand that specific test
7. Ensure the test succeeds or fails for the right reason
8. Prefer literal strings and in-line constants over pre-defined constants
9. Don't use production code in your assertions. The goal is to protect the contract, not to keep the test refactoring friendly.
10. Only assert what's relevant for that test case (e.g. Using anonymous types, string wildcards, etc)
11. Make sure the assertions provide enough information without having to go through the debugger hell

Naming and organization
1. Postfix your test classes with Specs to emphasize the specification part
2. Group tests by API or purpose using a nested class
3. Use a short fact-based name
a. https://www.continuousimprover.com/2023/03/test-naming.html
b. Don't include the names of code elements, classes or methods in them
c. Avoid should/then
Make sure it focussed on what the test is supposed to validate, not how it does that

Dennis Doomen

Hands-on architect in the .NET space with 27 years of experience on an everlasting quest for knowledge to build the right software the right way at the right time

The Hague, The Netherlands

Actions

Please note that Sessionize is not responsible for the accuracy or validity of the data provided by speakers. If you suspect this profile to be fake or spam, please let us know.

Jump to top