This week I’ve been working on an integration to an external provider used for newsletters subscriptions and I’ve realised that if there’s one area where TDD is a match made in heaven it’s when you're doing API Testing.
This was an API I’ve never worked with before with poor documentation and it was overall inconsistent so I knew right from the start that this was going to take a lot of trail and error. The only thing I knew really going in was how I wanted it to work once it was all finished.
What is TDD?
TDD (Test Driven Development) is a test-first development approach where the you start with implementing the behaviour of what your code will do before it does anything. As soon as you’ve written a failing test you’re “allowed” to write as little code as possible to make that failing test pass. And then you refactor that test before you jump on to the next test.
I love TDD but there’s a lot of different options about it amongst developers and it’s not everyone’s cup of tea. TDD is a coding style more than anything and it’s not for everybody. As long as you’re writing tests it doesn’t matter to me how they got there.
TDD is a coding style and it’s not for everybody.
For me personally TDD works great because it gives me constant feedback that I am going in the right direction and also it makes sense for me to start from how I want it to work in the end rather than to start thinking about architecture or which components I “might” be needing down the line.
So the first thing I do before I write a single line of production code is to setup a new test class for this integration. Then I find the most basic requirement for this implementation:
A visitor can select which mailing lists they want to subscribe to.
Sounds like I need to be able to fetch a list of mailing lists from the API? So I write a test for that minimum requirement:
Now at this point this is all the code I have. The classes and methods you’re seeing are completely empty, useless and are created just to make my project build. So obviously this test fails. Which is exactly what I want.
Disclaimer: Obviously checking if the result is null or not is not enough but thats covered in another test.
Now I can start to experiment with the production code and write as little code as I possibly can to make that failing test pass. It doesn’t even have to pretty at this point, hardcode whatever you need and try not to overcomplicate things. Just make it work.
I probably ran this test a million times (you know you can set your tests to run automatically every time you build your project, right?) and eventually: it passes. I got what I expected: A list of mailing lists back from the API.
And now to the best: Refactoring. Now I can start to think about best practices, good architecture, reusing components, move hardcoded shit in to the CMS and overall making my code look pretty with the comfort of knowing that my test passed before I started refactoring.
So my test should still pass after I’ve refactored it otherwise I need to go back and retrace my steps to see where I went wrong. When I’m happy with the way my code looks I move along to the next minimum requirement.
Days later as I am writing this post I consider myself done with this integration and I now have green tests covering every single API request in my implementation.
These tests run continuously on our build server and will ensure that if anyone would accidentally break something or the API would surprisingly change the project won't build and cannot be deployed to production if not all tests are green.
This integration turned out to be quite a smooth ride and I think it was much thanks to TDD. Since I didn't really know how the API worked from the start it made a lot of sense starting from how I needed it to work in the end and work my way backwards.
Hope you enjoyed it. ❤️