› TDD Practices
TDD has many practices that should be used in order to make the most use of the TDD approach. These includes:
-
Naming Conventions
There are many naming conventions that can be used, the important thing is that you and your development team use one. Some tools expect you to use some particular naming convention. An example of a good naming convention practice in java is that you should place test cases along with the code they test under one package. This makes looking for tests faster and easier. Also, it is a good practice that you give meaningful names to your test methods. This makes understanding the test easier.
-
All tests should fail the first time they run
Do not start writing the code for the functionality you want to implement until the test you wrote for that functionality fails the first time you run it. There could be two possibilities for a test that does not fail when you first run it. Either the functionality is already implemented or the test is broken. Therefore, when you write a test, do not start writing the code until you make sure that the test fails.
Rerun all tests whenever a change is made
Whenever you make a change to the implementation code, all tests should run a gain to make sure that the changes you made did not break the code in someway. This also applies to refactoring, when a code gets refactored, all tests should run again. This is extremely cheap in TDD since all tests are automated and that some testing framework takes care of running all tests for you. This also encourages refactoring since you will always find out if you accidentally broke the code so that you fix it right away.
-
Do not write a new test unless all tests are passed
Sometimes it is tempting to leave behind a code with failing tests and start developing new test for a new feature and worry about the broken code later. This should never happen in TDD as it will make fixing the functionality more expensive because later you will have a larger system, and you want to make sure your functionality is working right when integrated to the system. In TDD it is a requirement that you always have a working code with passing tests during the development process. Therefore, if a test is failing, fix the code and make the test passes, only then start writing a test for a new functionality.
-
Do not start refactoring until all tests are passed
First things first, first worry about implementing the functionality and making sure you have implemented it right. Then, worry about refactoring the code and making it better. This also helps refactoring the code in a safe way, the code was working before refactoring, and it should also be working after refactoring.
-
Keep design simple and clean
Simplicity is a key feature in all of agile processes including TDD. Do not worry about implementing functionalities that you THINK you might need in the future. Also, write the simplest code possible to make the test passes. When you have a simple code, changing it will be easier and cheaper. Therefore, do not make your system complex when it can be simple.
-
All tests should be independent of each other
All tests should be independent of each other. You should be able to execute one test without having to run another. This independency is a requirement in such approaches because otherwise you can not keep this dependency given the large amount of tests you still need to develop.
-
Write tests that run fast
Tests that take a while to run discourage developers from running them. As being said earlier, whenever a change is made to the code, you need to run all tests whether they test the changed code or not. However, if tests take a long time to run, you may become tempted to run only the tests that could be affected after changing the code, and this is not what TDD is all about.
-
Use mocks to simulate dependencies
In order to write tests that run fast, you need to mock dependency on systems that are outside the function being tested. For example, if your function reads from a database, do not let the test read from the actual database. Instead use mocks or interfaces in your test to simulate the behavior of databases and other services that your function need to access.