Effective TDD In Action
I’ve never been a complete believer in the purist form of the Test Driven Development (TDD) approach where developers first write failing tests and then the functional code to achieve passing tests. Instead, I opt for a more practical and pragmatic mantra: “write the code and tests at approximately the same time.” Write code, add tests for it, refactor the code, update the tests, write more code, write more tests, refactor the tests, refactor the code, etc. It’s a practical paradigm that aligns with the dynamic nature of development. It’s the 80/20 rule in action. Let’s get most of the value from the process and minimize the headache.
Enforcing TDD on a team or organization isn’t my style. After many years of championing unit testing initiatives I’ve just come to accept that unit tests are an uphill battle for your average development team. At least for the teams for which I’ve been responsible.
Here’s a reason to practice TDD that you probably won’t read anywhere else: writing code and tests at the same time is the only way to guarantee the developer is producing testable code. Writing testable code means the code can be called from multiple “entry points”. The first entry point is the actual application be it a web application, system service or something else. The second entry point is the test runner. The only way (and the easy way!) to produce testable code is to write code and tests at the same time. Doing so allows the developer to easily see when the code can be run only from the application entry point. We are only human. I’m sure you’ve been on an application that goes through a significant initialization process to setup all of the implicit dependencies that are needed just to run the darn API call that is the focus of our attention.
I’m really not a complainer. I’ve just accepted my reality and learned how to best move forward on a tricky and nuanced topic. Below is my playbook to add unit testing to any software team on the planet. The leader can hold the team completely accountable to ensure tests are added once the below steps are complete.
What can we learn from a skilled prompt engineer that can be applied to our human interactions?
My next post: Prompt Engineering for Humans- Give your team the ability to write unit tests and make it as easy as possible. Ensure that there are test projects in source control and the developers know how to run the tests. Add this to your onboarding process. Show the developer how to load the tests, add new tests and run them. Show the developer how to see the test results from the automated builds/pipelines.
- All unit tests must be run in the CI/check-in process. Running tests through the automated check-in is the only way to guarantee the tests continue to run in the future.
- Developers must have the ability to see code coverage on their developer laptop. Otherwise, they are flying blind to know if they are writing too many or too few tests.
- Hold the team accountable through code coverage minimums.
Remember, writing tests and code at the same time is the only way to guarantee that the team is writing testable code. Even if you do all of these things it’s still not a guarantee the team will even bother to write tests. You can make it available. You can make it easy. Now the team must be held accountable. I’ve seen exactly one team in my career establish long-term and continued success adding tests: we made our CI process enforce a code coverage rule that code coverage cannot decrease. Whether we were at 5% coverage or 85% coverage we blocked completing pull requests if the changes would reduce code coverage.
None of this information is a magic bullet. But it’s the only way I know how to do it and have confidence that we will get results.
Good luck.
On Twitter/X I post focused content on software engineering and leadership. Where did I get it right? Where did I get it wrong? Let me know.
Follow me on Twitter/X