I won’t cover the basics of TDD. I do want to talk about the advantages of writing automated tests (unit tests and integration tests) in general, and why applying TDD ,with a little twist, should be a standard in your company.
First, why should you write automated tests?
“In order to test and verify my code?” Daaahh! This is so obvious that it’s not worth mentioning! I think that unit tests and all kind of automated tests have many more benefits than just testing your code.
Better code design – If you have ever written unit tests, you’ll know there is no (pretty) way of testing or mocking static objects and tightly coupled classes which violate the SOLID principles. A testable class is a well-designed class that is loosely coupled from its dependencies, uses dependency injection and rarely has calls to static methods. A testable class is a SOLID class which will improve maintainability, readability and extensibility.
True documentation of the code – You’ve probably written a design paper at least once for a feature or any sort of documentation describing the code. Be honest – is this documentation still relevant? Does it really describe the code that is now in production? Probably not. The automated tests, on the other hand are the true documentation - since if the code breaks the tests, the code will be repaired or the tests will be modified to support the new behavior that has been added. Either way, the tests tell the real story of the class, allowing any other programmer other then you to quickly understand what the class does and how it behaves in different situations.
Killing the fear of change – One of the programmer’s greatest fears is the fear of changing code. You never know which class depends on this specific implementation in a specific scenario. Usually, QA will let you know about it. But not your company QA – your customers (the real QA), who will get hurt and be angry due to your changes. However, if the code is fully covered with automated tests then there’s no fear! You can add, modify and even, so help me god, delete code - and in a matter of a few minutes you’ll know exactly what logic you broke, and why (the tests assertion should be really clear).
A standard for your company
Your company is a business. It has competitors who are breathing down your neck, trying to get hold of your customers. Time and quality are crucial! You must deliver new features fast and with the highest quality so that your customers will be satisfied.
The only way to succeed is by writing automated tests that cover your entire code base and all the user flows. By doing this, you allow every single developer in your company (seniors and juniors) to quickly understand how the code works by reading the related tests, easily changing the code - since it has a great SOLID design – and having immediate feedback if any modification has ruined the existing logic, providing the exact places where it happened. This saves time and money, and should be the standard for every new feature in your product. It should be a fundamental principle for writing code in your company.
Why TDD? Just write the tests afterwards
Why should you use TDD if you can first write all the necessary code, implement the feature and only then write the tests?
Writing tests after implementation is way better than not writing tests at all. However, it might not get you the same effect as I’ve described above. When you are finally done with the implementation, the thing you want the most is to declare the feature as “done” and ship it to production. You probably won’t invest enough time in fully covering the feature with good, meaningful tests. If you encounter something that is fundamental in your implementation that is not testable, you will probably just leave it like that and not change it - leaving it untested.
The last thing is that you are biased toward your implementation: in your opinion, the way you’ve implemented the feature is the right way. Thus you will write tests that support your implementation and not necessarily cover all the edge cases and business goals that the feature was supposed to do.
TDD – the cherry on top
In order to truly enjoy the benefits described above you have to apply TDD -with a little twist:
First, start with an integration test for your feature – a test that will fully test the behavior of your feature. Some might call it “Behavior test” as in BDD. I think that is just semantics.
If you are fixing a bug, your tests should create the environment where this bug is recreated and test that the bug is prevented before you’ve actually fixed it! You want to be sure that you are able to reproduce your bug. Only then can you start actually fixing it by writing the tests first. Same goes with a new feature - first an integration test that checks the behavior of your feature, and only then begin to implement.
This way, you begin with the business goals and stay focused on them while you implement the whole feature.
Another great benefit in starting with the integration test is that you will have to create the setup for the feature. Doing so will make you truly understand how the feature works and on what it depends on. This is crucial before you start to implement, so you won’t make unnecessary mistakes.
It’s not easy: it requires strong testing infrastructure and patience – it’s hard to hold back the lines of code you are just dying to type. But it is so worth it.
If you want to easily write code at your company and not be woken up in the middle of the night to solve a bug in production, just write the tests. Better yet – start with them.
Great blog Denis!
ReplyDeleteI agree with every word.
I have 2 teams woring on new component - one in JavaScript and the other one in Java. Any recommended practical books for TDD for those programming languages?
Another quick question - I have a few other teams working on C++ code of old product - on the scale of 500K LOC (Line Of Code). Did you have any expirience with such case? (meaning entering TDD methodology into high-scale old code)
Thanks,
Erez
Hey!
ReplyDeleteThe best book for learning TDD is "Test Driven Development by Example" (http://www.amazon.com/Test-Driven-Development-By-Example/dp/0321146530)
The principles apply to all languages.
Regarding your second question - yes, I had such experience. TDD is not the issue - writing unit tests is. Usually legacy code is not written in a testable way which makes writing tests extremely difficult. I order to succeed writing automated tests in legacy code you will have to invest some time to improve the code and make it more testable - it comes down to ROI. If those teams keep on working with this code and keep getting bugs and afraid to change it then you should invest the time in it.