TDD?
An individual review article for PPL UI 2020 course
What?
What is TDD? TDD is an approach and style of software development where tests are made first before implementing any code functionality. The tests done are for every small functionality of the application. This means the tests will fail first and only then will any code implementation be done. Additionally, only enough code is made to make the tests pass. Any further code should wait for the next set of tests to fail first.
Why?
As programmers, we are bound to find bugs and failed tests that leave us confused and frustrated. Especially when we are already late in the development stage, where the source code has grown considerably large. This is where TDD can offer a much better coding ‘lifestyle’. In a bit, we will learn how to apply TDD in our work, but before that, let us see what kind of benefits we can gain:
- Early bug detection
By testing first and coding the functionality to make that test pass, we can analyse and find what kind of bugs may appear and be sure to avoid them in the first place. Since we do not continue with our work until the tests we made have passed. I have once experienced this benefit firsthand when doing my work. After making the tests and code implementation, I ran the tests again before pushing my code. Turns out, I have made a typo in the title of one of the pages, which of course caused a test to fail. Thanks to the early detection, the typo never reached the staging branch or even user branch! - Better design & cleaner code
Through TDD, developers will have to give more thought into how the code will be built. They have a clearer goal in mind and look ahead to what each code is really for. This way, each code will have a more singular function and therefore be cleaner. Your code will also have clearer structure since you have already kept in mind what you need to code based on the tests you have made. Moreover, code duplication is reduced and the DRY principle is applied. - Confidence
Since you only make progress when you are sure the tests have passed and your code is refactored to cleanliness, you will have more confidence that your code is bug-free and less fragile compared to when you don’t have tests yet. Besides, there is a different kind of satisfaction in seeing the tests pass and that “100%” code coverage indication:
- Easier maintenance
When we make sure all our tests have passed before doing something else, we can be more sure that our code up to that point is bug-free. IEEE Software once stated that implementing TDD reduces the percentage of bugs in our code by 40–80%. Add that with the clean refactored code that TDD practice has, less time will be needed for debugging and reanalysing our code. This will then lead to time cost efficiency as our application matures and project reaches late development stages.
How?
So how do we apply TDD practice? Simple! there are 3 stages in the TDD cycle:
- [RED]
The first stage is the RED stage, where our tests fail and so will our pipeline. Our pipeline will fail, which is the goal in this RED stage. Not to forget, it is best that our tests contain both positive and negative tests. The commit message here should include the “[RED] …” indication. - [GREEN]
Next is the GREEN stage, where we write just enough code to make the tests in the RED stage pass. The commit message here should include the “[GREEN] …” indication. - [REFACTOR]
This stage is the last of the 3, where we refactor the code we wrote. The goal here is to reduce any code duplication, code smells, etc. This is where we make sure our code is clean and not to forget, be sure to keep the tests and pipeline GREEN as well. The commit message here should include the “[REFACTOR] …” indication.
Additionally, the tests we make should best follow the FIRST principle:
- Fast
The tests should be fast when executed. Slow running tests can cause delays in detecting bugs early or could mean they are too big. - Independent
Tests should not depend on each other, meaning their execution should not depend on another test’s execution. This way, you make sure you know exactly where something went wrong. - Repeatable
Your tests should be able to be repeated many times along development and not in just the local environment. - Self-Validating
Tests should have assertions or validations on whether they fail or succeed. You shouldn’t depend on the logs produced by the tests to analyse it. - Timely
This means the tests should be written before code, obviously. Since we are talking about TDD here.
Implementation
Our team has been following this practice from the start of the project. Here are some screenshots:
- [RED]
- [GREEN]
Here is an example testing process I’ve done in the back-end part:
In the RED stage, this is a simple example of one of the tests to make sure the correct status code is returned upon success of adding the details of a facility information. This test is surely Fast and will finish executing in a matter of seconds. It is Independent, since it is not influenced by other tests. It is Repeatable, it can be repeated at any time in the development process after creating it. It is Self-Validating as seen with the assertEqual function that contains an expected output. Finally, it is Timely, since this is done in TDD manner, it is written before the implementation code.
In comparison to front-end testing, such as:
Turns out, backend testing is much faster for me to create since it is more clear to me what needs to be done. Front-end testing requires more ‘imaginative abilities’ to properly create thorough tests since it is mostly functional tests. Also, I encounter much more trouble in front-end testing than back-end where in most cases, it directly succeeds. If not, it is easy to debug and solve any errors.
Then in the GREEN stage, the relevant code for the above example test is shown. The response return contains the status, which is what we tested above.
Thank you for reading!
Thank you for reading!
— Wilson
References: