TDD - When It Works And When It Does Not
Test-driven development (TDD) is a way of developing software in a way where tests drive the implementation. This means that before you develop any real implementations you have to fully understand your business logic and be able to formulate the success, failure and edge cases into tests that assert the correctness of them.
- Shorter Development Cycles with less bugs
- Better Software Quality
- Better Developer Confidence
- Better Developer Experience
- Faster Developer Onboarding
- Time Consuming if developers are new too it
- More code that requires maintenance
- Continuous Integration that requires maintenance and costs
How To Get Started
Getting started with TDD can be a daunting venture if you’ve never done it before. Most developers are used to be given a task, implement it and eventually add some tests to cover a few cases that assert that the implementation is working as expected.
The problem with this mindset is that you are looking at the bigger picture as opposed to building up piece by piece like with TDD, which eventually will result in you overlooking something and more easily introducing a bug. There are a few things that I think can help you to easily get started with TDD and it evolving into a natural part of your daily workflow.
Write tests for everything, no matter how small. Writing tests for everything you develop, no matter its size, will turn into a habit and at some point you will just write tests first without even thinking about it.
Browse other peoples test suites. Digging through other peoples test suites will help you to discover new tools, best practices and nifty tricks. Learning from others work is a great way to move forward fast instead of trying to figure it all out on your own.
Dig into the testing framework of your choice. Most modern testing frameworks like PHPUnit, RSpec and Jest have great documentation but documentation can only go so far. All those frameworks have hidden gems that are not documented because they were either forgotten or aren’t useful for the 99% use-case. My recommendation would be to dig deep into your testing framework of choice, find those gems and make your own judgement if you find them useful or not.
Avoiding TDD Hell
All of this sounds great on paper and you should definitely make use of TDD as much as possible but like with everything there is a time and place for it and that means there are also times where TDD can stand in your way and cause problems.
Lets say you are building a job queue. That’s a perfect case for TDD because you are in control of the system and know the requirements so can easily write various tests to ensure that the happy and unhappy paths are working as expected. This becomes a whole lot more difficult once you start working with data from a third-party service.
If you aren’t in control of the system and rely on responses from other services this becomes a bigger challenge. You need to figure out what headers need to be sent, what request parameters are expected, how the response will be shaped, possible errors and data malformations. In cases like this TDD can stand in your way and it will be more efficient to first write the code and get a working end-to-end solution working before bothering with tests.
Once you have gained confidence in your solution you should get back to writing tests but don’t make the mistake of starting with the biggest parts. Start with the small bits to gain confidence and slowly work your way up the ladder to the more complex parts of the system.
Test-driven development encourages you to design simple systems and raises the confidence of all developers involved but isn’t always that black and white. If you don’t think that the solution you have in mind will be the final solution you just end up wasting time on tests that will be discarded.
When working with large legacy codebases or having to expand an existing system without extensive documentation it can often be more beneficial to first try out a few implementations before deciding. So don’t sweat it if you are trying some solutions without tests, manual testing is still a thing and more effect if you aren’t sure your idea solves the issue.
Practice is the only solution to gaining confidence and writing good tests, so keep writing tests until you don’t even think about them anymore but just write them as another part of your application.