In this post I will try to list some of the mistakes I made while practising Test Driven Development(TDD). I will partly blame my mistakes on how the information about TDD was disseminated and how I (mis)understood it. I started to follow some of the TDD principles back in 2009 and one of the point I strongly agreed to was the need of writing automated unit tests. After reading through tons of articles and mixing my intuition with this newly gained knowledge I started to write code which I believed was a TDD style but in reality it was not. I ended up writing the code which was not following any of good object oriented programming practices.
So here goes my list of mistakes I made while boarding TDD train:- Writing tests after code: This is the the very first mistake I made and I have seen many people making this mistake and believing they are following TDD. It was difficult for me to think of test beforehand and write the code later and I reverted back to writing code first. Majority of time, writing tests later
- Makes them complicated and difficult to read
- Makes them brittle and encourage you break encapsulation or other OOPs principle to increase test coverage
- Encourage you to count the asserts
- Tends to increase the size of setup
- Abusing the power of interfaces and DI to facilitate unit test: This one is very interesting. I got the impression that with the power of interface and dependency injection I can unit (class) test any code at any time then why bother writing test before code. It is precisely because of this misunderstanding I ended up writing code first. While following this approach vigorously I ended up creating interface with just on method. This approach is no way near the good OOPs let alone the TDD. I have blogged about it in details here.
- Assuming a unit test is always equal to class test in every situation: This is another wrong impression I got while introducing myself to TDD. Many of the online articles were explaining unit testing using an OO language like C# or Java and almost all of them used to emphasise that -
- Unit test for a class should focus only testing of behaviour of one class
- As soon as class under test interacts with other class, use mock and stub
- Too much mocking: I completely submitted to the idea that unit test should focus only on one class and the moment it start using collaborating with other objects I should use stub and mock straightway. As a result I was writing the test where a mock was returning another mock. My unit tests were brittle and were breaking on slight refactoring. I can write a separate post on perils of too much mocking. These days I'm using stub and relying on object state and identity/equality for state verification and occasionally using mock when my system is trying to communicate with other system and I am quite happy with readability and reliability of unit test cases.
- Blanket ban on usage of static methods/properties: To promote unit testing, programming to interface and dependency injection some posts has almost advise not to use static method/properties. On of the primary reason for not using static method/properties is that they are not good at being stubbed or mocked. But it depends on what kind of code you're writing in static method/properties. If you're writing behavioural unit test and your target code is using accessing static method/property then it will tested along with it.
- Counting the number of assertions in test: If you're counting the assert than probably you're writing code first. Because if you are writing a behavioural unit test before the code than you tends to write all the necessary asserts to validate the behaviour and generally do not count them.
- Try to get 100% unit test coverage: I had given too much importance to code coverage and tried all the tricks to increase it. But in reality coverage does not tell you anything about the quality of unit tests or code itself. I have seen that people even break encapsulation to increase test coverage.
- Open up internals of target code to ease unit testing: Encapsulation is the holy grail of OOP and needless to say that code becomes unmaintainable if it is broken.
- TDD is the only way to write quality code: In think that is where TDD was oversold (mis-communicated/misunderstood). Alone writing of test first does not guarantee the quality of code, you need good knowledge of OOPs (if you're programming in object oriented language), design patterns, SOLID principles and willingness to represent domain language in code. Even if you're writing the test first but does not have good understanding of good programming practices then quality of your tests and code are going be very poor. I have written a blog post to highlight this issue further. In this post I have tried to show that even though you are writing test first and programming to interfaces and using dependency injection all over the places but the quality of your code and unit test is still very poor. Real problem is here not TDD but how the programmers have (mis)understood it.
- No upfront design: When someone mentioned that programmers should not waste too much time on upfront design and let your tests discover the it, I (mis)understood as- I should not spend any time of design instead should straight go to writing the tests and let my tests discover it. But in reality you need some design (not the design of whole system) then only you can write the unit test to validate/refactor it. By design I mean you need think of a class and what it represent in your system and what should be its role and responsibilities. You need to think about its behaviour then you will write the test first to validate your design and understanding about this class.
- Inhouse training in OOPs by Fred George at Cloudhouse.
- Feedback and criticism from my colleague at Cloudhouse
- Practice, practice, practice.. apart from try out the ideas on company projects I worked on my personal open source project at my own pace.
Excellent knowledge sharing guru!!!
ReplyDeleteAlways inspiring
Hi Ashish, I am glad that post was useful to you.
Delete