Do You Really Understand Test Driven Development?
How TDD Is Seen?
TDD is a great technique, full of benefits. But this is not evident to everyone. For some people, TDD is a practice that any professional should practice; for others, it is a waste of time. It seems that there is a chasm between both profiles.
I believe that I got lucky; I have been on both sides. I started as a test hater. Well, not a hater, but I was unable to understand the value, so I saw it as useless. Later I had the opportunity to put it into practice with great mentors, and since then, my appreciation has not stopped growing.
I have met people that hate TDD or do not see the value. I have met people that have started TDD with great mentors, and they do not understand how not everyone does TDD.
Furthermore, I have met people that like to do TDD, but they practice it wrong, and although they lose a great part of the benefits, they still do it. And of course, I have met people “forced” to do TDD by its organization, and as Robert C. Martin warned once, they find imaginative ways to comply without delivering its value.
The Two Practices of TDD
The first time I started to understand TDD, and when I finally saw its value, others told me: you are not doing TDD. You are doing BDD instead. In those days, I did not know what BDD was, but for me, I was doing TDD as I had been taught. So, as you can understand, that remark puzzled me. I kept working the same way until I met another colleague, very self-confident, with a different TDD approach. Because we were on the same project, and both were sharing the same code, we needed to achieve an understanding. But, instead of imposing a way to work from one to another, we chose to run an experiment. During a few months, two types of tests and TDD were coexisting in the project. Inevitably, specs changed, the code needed refactors, and we discovered something interesting. My TDD tests resisted refactors, but his tests needed to be thrown away and rewritten almost every time.
What was the difference between both practices? The tests. While I was testing behaviors, he was testing the code. It seems strange; in both cases, you are executing the code, but the difference was the intention of the test. As you can suppose, by then, I did not say test behaviors, but I said that the test should be the documentation. While my tests were a guide about how to use the application, he tested functions and code. The difference was small, but the impact was huge.
Years later, I realized that most programmers think TDD should test the code, not the behavior. And that is a big mistake.
QA Unit Tests
When people test the code, they start asking the wrong questions: Do I have to test the public methods or also the private methods? Do I have to test all possible values of the arguments for every function? When you deeply understand the TDD, the answer seems obvious: the code does not exist before the test, so there are no public or private methods to test, and there are no arguments. But this is not obvious because we have been taught differently.
Looking for the root cause of why most programmers test the code but not the behavior, I came up with one clear answer: they are borrowing QA concepts. It turns out that QA was born inside the Waterfall methodology. In any Waterfall organization, once the developers have delivered the code, the testing team comes and tests all the code. They copy the model of an assembly line, in which thousands of copies of a product are tested by stressing the weak points. The Waterfall testing assumes that we already have the code and looks for cracks in the code. Thus, the QA testing methodology defined three levels of testing according to the type of code they are testing. This is how the testing pyramid emerged and how we all learned about Unit Tests, Integration Tests, and End-to-End tests.
In general, when new programmers learn to do TDD, they are taught to do Unit Tests as defined by the QA practice. QA Unit Tests are focused on pieces of code, not on behaviors. These tests mock everything, so they are useless for code design changes; and are very coupled to the code, so almost any change in the code implies a change in the test. As a result, the code becomes rigid, tests become an impediment, and programmers feel TDD is a waste of time.
TDD Unit Test
The other option is testing the behavior. Instead of writing the test thinking in the code, we must think about the business rules. It does not matter if we test all the public methods or all of the possible function argument values; what matters are all the relevant use case examples.
That changes how we manage the code. The tests become decoupled from the code, and refactors are possible without changing tests. That allows the design to evolve, and if there is any problem, you always know which business rule you broke.
Unfortunately, we also call these tests Unit Tests, but they are very different, with different objectives. They got this name because Kent Beck, the inventor of TDD, was the creator of the first xUnit testing tools. Read More...