TDD, not just for Unit Tests
TDD (Test Driven Development) has been a buzzword in the tech industry for quite some time now. My first introduction to it was in mid 2000s, when I first read the book by Kent Beck, entitled Test Driven Development by Example. I loved the idea, which essentially means when developing software it is just important to think about how to test it as it is to code the application itself.
A key phrase to explain TDD is “red, green, refactor” which essentially means write a unit test that fails (which is indicated by a red bar on the developer’s screen), write only the code required to make it pass (which then changes the test to passing and the bar to a green bar), and refactor your code as necessary. Repeat this until all code is done.
I think of myself as a very logical person, and it makes perfect sense to me that all code I write needs to be well thought out and designed, even my unit tests. The Test Driven Development book gave many practical examples of how to write your tests before you wrote the code. This was enough for me to start writing my applications this way.
Another great benefit is the confidence I get when writing my applications, because after I am done writing my code, I have already incrementally tested it and there isn’t a lot of time spent afterwards writing tests. It also makes refactoring my code much easier, because I know if I broke existing functionality then my unit tests will break as soon as I run them.
With open source projects such as infinitest (http://infinitest.github.io), as soon as I save my code the tests run automatically! This provides me the confidence and short turnaround time needed to develop software in an efficient manner.
Other benefits such as writing testable code and reducing the need for manual testing are also quickly realized.
As the Application Testing Infrastructure Architect of my organization I have the opportunity to talk to a lot of developers and testers about software development and testing. For many, utilizing TDD is a big shift that takes a lot of time to adopt and frankly to comprehend. As with learning most any new way of doing things, it takes a mind shift and culture shift. Most individuals that truly give TDD a chance, love it and never go back to the old way of developing.
When talking about TDD, it is generally referred to as writing unit tests before writing your code. I want to expand that definition some and say truly writing TDD includes not only unit tests, but also API tests as well as end to end functional UI tests too.
The same benefits described above can be applied to other layers of the testing triangle (https://martinfowler.com/bliki/TestPyramid.html) as well. I also think it has several desirable side effects that I would like to go into.
First, the direct benefits. Just like with unit tests, you test and develop incrementally which allows you to focus on one capability at a time. It provides the confidence as you write the application that you are building it in a robust and testable manner.
Now, the desirable side effects. I still find in many organizations that I talk to, a divide between developers and testers or developers thinking they should only do unit testing. By utilizing TDD at every layer of the testing pyramid, this enables a much tighter feedback loop between testers and developers, because the work must be done in tandem, which requires the developers and testers on a team work closer together. It may also have some developers decide it makes sense for them to write the tests themselves and utilize the tester’s expertise for what tests to write.
For TDD at the API layer, this allows for much more thought to be put into the endpoints and what they should look like, since the developer is thinking about how to test the API before writing it. For example, if writing a RESTful service to get customers by thinking like an end user (or client of the API) rather than just thinking about how they are going to write the code, they may make the endpoint more self describing or logical.
A good example to start TDD of an API is write the test to call the endpoint before it exists. Then write the end point with no code to get it to pass the test. Next, apply a security role requirement to get it to fail. After that, enable the role on the service, which would then cause the failing test to pass.
TDD can also be used at the UI layer to help the developer consider usability. Much like the mental model the developer has with the API, when writing tests first, they will be looking at the UI from the perspective of the end user and not how they want to code the page. This could lead to a more useable page and page flow.
Finally, by UI Tests not just being an afterthought, the test code can be written in a more modular way, for example using the page object model pattern.
Test Driven Development (TDD) has been a very valuable pattern of development and by utilizing it at all layers of the testing triangle, teams can be even more collaborative as well as more robust.
Would love to hear others thoughts and experience with TDD as well as perspective of how it fits into the other levels of the testing triangle.
Great explanation Bryan. TDD and BDD must be formalized and more automated solutions required for different platforms. And enforce thru ALM.
Great article Bryan! TDD has helped me to grow tremendously as a developer. It was indeed rough at first, but was well worth the learning curve. The “aha” moment for me was when I was iterating over a unit test for a class that required multiple mocks and stubs. I just couldn’t figure out why it was so hard to write the unit tests. Then I took a step back, thought about why? And it was because what I was writing was not testable. In this way the TDD methodology discourages poor design. The tests meld the implementation into a more modular and testable design!
Nice article!