“Testing before the software is done? What a silliness? It is totally crazy. It goes against the theory of testing. These guys should learn first how to develop software.” I heard these sentences from a software architect who had around 20 years experience in the software business and I think he was a really good professional, he was just a bit conservative. That time I did not have enough experience to have my own opinion about the topic. Now I already have.
The Test Driven Development is one of the buzzwords in the world of programming which became popular during the last years. Each fancy company is advertising itself by being agile and doing TDD. But what does it exactly mean? Does it really change everything what we learnt about software development?
Based on Wikipedia: “Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: requirements are turned into very specific test cases, then the software is improved to pass the new tests, only.”
Practically that means that is you are developing a peace of code you are creating the code and its unit test parallel. First you are implementing one unit test case, which is testing against the first requirement against your code. Then you are running your test case which will of course fail. Now you are implementing a peace of code which is making your test case green. Then you are implementing your next test case and extending your code in a way to pass the test. If you think that you could change your code to be cleaner just change (refactor) it and check if the tests are still green. Repeat the test until your code is not fulfilling all the requirements.
Let’s see how do we develop by default? We are writing a piece of code. After a while we are trying to run it to check if it’s working. If it is a library or something that is difficult to just run we are often implementing a short demo code which is not doing anything else just invoking our functionality with some predefined parameters and we are using it to check if our code is correct. In other cases we are figuring out a certain scenario in our program which is invoking are functionality and we are doing this scenario every time we want to check if our code is working correctly. If our code is not doing what we are expecting then we are changing it and run it again. Once it is doing what we want we are extending it with several new lines (some new functionality) and checking if it is working. We are repeating these steps until your code is not doing exactly what we want to have.
Are you developing in the same way?
I think most of the developers are doing something very similar.
Is it similar to test driven development?
Yes, there is some similarity.
- Run the “test scenario” often takes long
- You are always testing against the same input parameters
- You are always concentrating only on the last functionality you added
- If other developers change your code later, they won’t have any idea what should your code do
Sometimes it is also required to implement unit tests (due to project requirements or whatever). So that you are trying to implement some unit cases to an already existing code. You are implementing them in a way that they will pass for sure. Furthermore if you code is not structured properly it can be really a nightmare to cover it by unit tests.
It is basically nothing else then hard coding your “test scenario”. Instead of manual tests (running your scenario manually and checking yourself the results) you are using automatized unit tests.
So first you are figuring out what should your first piece of code do. You are implementing your first test scenario in a unit test. Then you are coding it and checking your test scenario. If it is not working, then you are fixing your code, otherwise you are extending your test scenario (by adding new test cases) and implementing it.
Sometimes it is also required to implement unit tests (due to project requirements or whatever). So that you are trying to implement some unit cases to an already existing code. You are implementing them in a way that they will pass for sure. Furthermore if you code is not structured properly it can be really a nightmare to cover it by unit tests
The only point is that in the classical way most of the cases first I’m implementing and then I start to think what my code should really do. In case of TDD first I need to define what should the next piece of code do. Here I’m handling it a bit flexible and sometimes I’m just implementing the code first and then adding the new test case. I know, it is against TDD, sorry.
By using this way of development:
- You can tell that you are doing TDD
- You can check if your code is doing what it should by one click in some short seconds
- You don’t need to repeat the same manual test scenario always
- You can easily set different input parameters to the all the corner cases
- You are always running and concentrating on all test scenarios, so you realize for sure if you broke some previously implemented functionality
- If anyone is changing your code in the future, they can check if they did not break the original functionality of your code
- Your code is for sure well-structured to be testable, which is one criteria of the good code quality
- You can anytime refactor your code without taking the risk that you are braking the functionality
- You have unit test with a good code coverage
The only disadvantage I can mention is that implementing good test cases takes time. But running manual scenarios 1000 times also takes time…
There’s no black magic, right? You don’t need to change everything in your current practices.
In the classical development processes (waterfall, V-model) there are requirements. The software development team is creating a software design from that and they are implementing it. After that a dedicated test teams checks if the software is fulfilling the requirements. It is really important that the developer and the tester should be two different person. So that if one is misunderstanding a requirement the other will realize it.
But the requirements are usually high level functional requirements, so they are not tested by unit tests, but by some higher level tests, like system tests, components tests or integration tests. So the tests done by the test team are on a higher level. The unit tests are usually done by the developer team based on the software design what was also done by the developer team. In 99% of the cases it just means to implement test cases which are passing on the already existing code.
TDD is replacing this kind of unit testing activity. But it doesn’t have anything to do with the higher level testing. I think such tests still makes sense before a software release, so good news: you don’t need to leave your classical testing process, it is not against TDD.
But let’s see further: what happened then at testing phase? The test team finds a bench of bugs, creates bug tickets and send them to the development team. The development team is fixing the bugs and creates a bugfix release. The test team if testing again and realize that some of the behaviour is still not correct, so a new bugfix release is needed. To improve this process the higher level testing can also be done (at least partially) by automated tests. And it is also a nice idea to implement these automated tests before the software development is done and provide them to the software team. So that the software team can run the test cases on its own and in case of failing test cases they can fix the code before releasing it. So that they can save a lot of time both for themselves and for the development team.
The very best that can happen is to run all test cases in the continuous integration system, but it is already a next topic (maybe for next time).
You can see that test driven development does not differ much from the usual way of development and it also does not hurt the main points of the classical testing approaches. It can take some time and requires practice to implement proper test cases, but it makes really a lot of benefits on long term for your project. So I can just propose to try out TDD, but you should always handle it flexible and don’t expect that it is solving all the problems of the software development.