TDD without tests first approach

Dec 15, 2021

Recently, I have watched a series on cleancoders.com where Robert 'Uncle Bob' Martin and Sandro Mancuso each develop the same application with two different approaches.

The approaches are not directly about TDD, but more about how to develop. Sandro Mancuso went with the 'London' approach of working outside-in from the API specification implementing controller logic and developing the inner workings of the application from there.

Bob Martin used the 'Chicago' approach of inside-out development of the inner domain and working towards the controller from there.

Sandro used the Mockito mocking framework extensively whereas Uncle Bob even abstracted the request handled by the Spark framework used in the application.

Pros and cons

Each of the methods have their benefits and disadvantages. What struck me when watching the series was how much more I liked the end result of Sandro Mancuso's approach. It was a structured step by step implementation of functionality, one endpoint at a time, backed by tests and without major refactoring or fundamental changes in the application apart from what was needed to add the next functionality.

Uncle Bob, on the other hand, abstracted and implemented much more functionality in order to be able to test without using a mocking framework. He still used mocks in his tests, but they were mocks created by himself.

It is fair to mention that the API specification and frontend application used for viewing the progress, was developed beforehand by Sandro, so of course he had the benefit of knowing both the Spark framework and the thoughts behind the API specification.

However, even when considering these advantages, it was clear to me that the application developed by Sandro Mancuso was simpler, more consistent and more clearly tested.

Applying to my own work

When reading the above introduction, you would be forgiven for deducing that I would be totally on board with the textbook TDD approach about writing tests first before implementing anything.

You would be wrong, though.

Not that I would neglect writing the tests and not because I would write the tests after all the code, but because sometimes, I want to explore and experiment with the code structure without constantly having to rewrite my tests.

If I have a clear idea about the API specification, I may have a high level test for the endpoint, but the TDD method of writing a test, implementing the code to make it pass, then refactoring and then writing a new test that fails, seems to me to be restricting my creativity.

With a high level API test, I can quickly get to a green test and then refactor until I am happy with the implementation - and THEN I will do the unit tests to cover all scenarios. A handful of unit tests will be natural during the refactoring, but my focus is on ensuring that the bigger picture works and unit tests are only there to give me the confidence that complex and/or crucial elements work as expected.

Ensuring the code is covered by tests

The final step of covering all scenarios is a vital step. I have previously given my comments on using code test coverage as proof of all scenarios being covered and I still hold that code test coverage percentages alone is a dangerous simplification. However, it is an important part of the key to success: Mutation testing.

I will cover mutation testing in my next blog post.

by Jan Keller

Long time developer, architect and CTO with a real love for the backend. On this blog, I give out insight gathered through working with Vue, Nuxt, Laravel and more, when building and maintaining our SaaS.

Catch me on Twitter and Github

Restructuring a Laravel application using Domains, Actions and more

In coding tutorials, much is often left out in order not to be too verbose, to keep the complexity down or because it feels over engineering to follow through with the examples. I will give a full structural suggestion based on my experience that can be applied directly, regardless of the project size.

Jan Keller Jun 8, 2022

Mutation Testing - the missing link

Mutation testing can give your tests that extra confidence level not achieved simply by measuring code test coverage.

Jan Keller Dec 15, 2021

Lessons learned migrating to Laravel Vapor

When moving to Laravel Vapor, there are things to consider. In this article, I name the obstacles I have experienced in a recent project.

Jan Keller Jun 4, 2021

Tree hierarchies in Laravel

Three approaches to parent-child tree structures with relational databases.

Jan Keller Jan 1, 2021