With my first TDD experience, an insight came upon me. The last 2-3 years from all sides are attacking the information “TDD is good”, “You need TDD”, which is why the brain instinctively begins to resist the implanted information. Before that, I had never practiced TDD in commercial projects, but when I started it, everything fell into place. I want to show one interesting in my opinion point of view on automated tests, which I have never heard from anyone.
Over the past year, I have read several good books, not even on TDD, but simply on good programming / design practices that explicitly state “if you don’t practice TDD, drop everything and start right now.” But still, having re-read enough of the theory, it was not entirely clear where such insistence and confidence of the authors came from, that it is all right.
Why do you need a compiler?
Compile? I once realized that not only. More precisely not in order to get some kind of binaries, DLLs. While typing the code, I look out the window with errors from time to time and fix it if something appears. This is a wonderful opportunity provided by the parser, which turns in the background and checks the code for the presence of simple errors. But sometimes this is not enough. Sometimes the compiler needs to compile / compile all project sources to detect an error. This situation happens for example in ASP.NET, when we remove the public method from our usercontrol, but we don’t see any errors, since the compiler must reassemble this user control. In such cases, you need to completely rebuild the project, but note that we essentially do not need what we got as a result of the assembly; at this stage, only the information about the absence / presence of errors was important to us.
What am I getting at?
In addition, the compiler in this situation is mainly needed to point out our programming errors. Usually these are simple errors, but we love the compiler for what it directly points to them. Remember one of the downsides of loosely coupled architectures? Due to the fact that late binding predominates in them and instead of inheritance they prefer composition, we can not catch many mistakes while writing code.
')
class Darth { public void FeelForce() { ... } public void DoHeavyBreath() { ... } } class Luke : Darth { public void DefeatAllEnemies() { ... } } void main() { Luke luke = new Luke(); luke.FeelForce(); luke.DefeatAllEnemies(); luke.DoHeavyBreath();
Calling the
DoHeavyBreath method of the
Luke class is a logical error not visible to the compiler. This is a higher level error. But programming year after year seeks up, it becomes more abstract, so we need tools to cope with complexity. One of the necessary tools is a higher level error parser. Just like us, the compiler points out syntax errors.
The code above would be worth rewriting for more literacy:
abstract class Jedi { public void FeelForce() { ... } public virtual void DefeatAllEnemies() { throw new NotImplementedException(); } public virtual void DoHeavyBreath() { throw new NotImplementedException(); } } class Darth : Jedi { override void DoHeavyBreath() { ...
What changed? We all have the same logical error and the compiler still cannot detect it. But now we can use TDD to detect this error. We create a method to test the main method. Simplest way
void test_that_luke_cannot_do_heavy_breath() { try { main(); } catch(NotImplementedException ex) { Assert.Fail("Unimplemented method was called!"); } }
I personally see in this approach nothing more than a tool to deal with the complexity of high-level architectures. The brain has its limit, which it can cover, and the necessary tools expand these limits. At high levels of abstraction, the compiler loses its role as an “error pointer”, since it is unable to cover the main percentage of errors. TDD is able to. Of course, the technique will continue to evolve. Ideally, the developer should know all existing bugs (logical including) before he compiles the project, he will be helped by existing and future tools with which TDD is mainly associated. This development can already be seen. Anyone interested can read about NCrunch for Visual Studio. I think under other IDE such tools are also available, which relate to continuous testing. In a nutshell - this tool all the time in the background runs unit tests and you immediately see the test results as you write code without recompiling. You will notice some similarities with the way the compiler looks for syntax errors in the background and produces as it is found. And now the tools have become so clever that they also show logical errors. Of course, you can express your dissatisfaction with the fact that you still have to write the tests yourself and you can lose sight of some logical error, but I think this is still ahead. Maybe sometime tests will themselves be generated from some formal description of business logic. In any case, it is better to rejoice at what is, than to cry for what is not.