With software as complex as it is today, we can’s rely on manual testing to for every part of it. This is where unit tests come in.
One way to develop software is to write the tests first before writing the production code. This is called test-driven development or TDD.
In this article, we’ll take a look at what test-driven development is in detail and how we write tests along with production code.
Three Laws of TDD
There’re 3 principles of TDD:
- We may not write production code until we’ve written some failing unit tests.
- We only write a test that fails and not makes compilation fail.
- We write production code to make the failing test pass.
This means that the test and code are written together. The result is that we write dozens of tests every day and test coverage would be comprehensive.
Keeping Tests Clean
Test code should be kept to the same quality as normal production code. This way, it’ll be easy to maintain the test.
Things like naming things properly, creating functions that aren’t too long, formatting, etc., all apply to unit tests just as much as production code. This makes tests easy to maintain so people can move on to writing production code.
It requires just as much care in terms of design and implementation as production code.
If we don’t maintain our tests to the same quality as the production code, we lose flexibility in our production code since we don’t have confidence in our production code without the tests. We’ll fear of making changes because we don’t have tests to tell us that our production code isn’t creating bugs.
Clean tests are readable. It’s the most important part of unit tests because we have to look at them and know what they are testing.
Readability includes clarity, simplicity, and density of expression. We want to say a lot with a few expressions as possible.
Each test should follow the build-operate-check pattern. This means that we should set up our mocks if needed and set any real data, then we do something with them by calling the code that we’re testing, then we check for the expected result.
Since test code runs in a test environment only, they don’t have to be as efficient as production code. However, they do have to be fast so that we don’t have to wait too long to run all the tests since they’re going to be run as we develop the code.
One Assert Per Test
To make tests as granular as possible, it’s a good idea to have only one assert statement per test.
This makes our tests simple and we can change them more easily. It’s also more clear of what we’re checking.
Single Concept Per Test
A single concept is a better rule for unit tests. Tests are used to check one concept so everyone knows what each test is checking. There’re no surprises or hidden code.
FIRST is an acronym for the principles for writing tests. Each letter stands for one principle. They’re as follows:
- F for fast — tests should run fast. Slow tests are torture to run, so we won’t want to run them frequently. If we don’t run them frequently then we’ll miss regressions until we catch them later. Also, we’ll be less comfortable with code changes since we can’t verify the results by running tests.
- I for independent — tests shouldn’t depend on each other. They shouldn’t set up the conditions for the next test. This is because when one fails, then all the other fails, making diagnosis hard
- R for repeatable — tests should be repeatable in any environment. It shouldn’t matter if there’s a network connection or not. So for these kinds of things, we should mock them. We’ll run into problems when tests depend on external resources that might not always be available.
- S for Self-validating — tests should have boolean output, either they pass or fail. We shouldn’t have to read through logs to tell if tests pass. Failure becomes subjective otherwise and they require long, manual evaluation
- T for Timely — tests need to be written in a timely fashion. They should be written before the production code to make them pass. We might run into production code that’s hard to test if we write tests after.
Test-driven development is something we should consider when writing our code. We can write tests before production code to make sure there’s coverage for everything and that production code is easy to test.
Each test should run fast and are small. They should stick to testing single concepts and do it independently of other tests.
Tests should run in any environment the same way. They shouldn’t rely on external resources that aren’ t always available.
Test results should either be pass or fail. It shouldn’t be up for subjective interpretation by people.
Finally, the test code has to be as clean as production code so that we can read and maintain them easily.