When writing unit tests, developers try to follow the F.I.R.S.T principle as much as possible. F.I.R.S.T is a combination of multiple principles. Let’s learn what these are.
The first letter in the F.I.R.S.T principle stands for – Fast. Unit tests are small pieces of code that perform one, specific task. Because unit tests are small, and unlike integration tests, unit tests do not communicate over the network and do not perform database operations. Because unit tests do not communicate with remote servers or databases, they run very fast. Developers who practice the test-driven development, run unit tests very often as they implement app features.
The next letter in the F.I.R.S.T principle stands for – Independent. Unit tests must be independent of each other. One unit test should not depend on the result produced by another unit test. In fact, most of the time, unit tests are run in a randomized order. The code you are testing or the system under test should also be isolated from its dependencies. To make sure that the bug in a dependency does not influence a unit test, the dependencies are usually mocked or stubbed with predefined data. This way, a unit test, can test a system under test in isolation from its dependencies and produce an accurate result.
The next letter in the F.I.R.S.T principle stands for – Repeatable. A unit test should be repeatable and if run multiple times should produce the same result. If a unit test is run on a different computer it should also produce the same result. This is why unit tests are made independent of the environment and from other unit tests.
The input parameters that the function under test requires are usually predefined and hardcoded. And if function under the test needs to be tested with valid and invalid input parameters, then two or three different unit tests are created. Each unit test tests the function under the tests with its own set of predefined parameters. This way, the unit test becomes repeatable and can run multiple times in different environments and will still produce the same result each time it runs.
The next letter stands for Self-validating. This means that to learn if a unit test has passed or not, the developer should not do any additional manual checks after the test completes.
The unit test will validate the result that the function under the test has produced and itself will make a decision whether it passes or fails. After the test completes, the result will be clear.
The next letter stands for thorough and developers who practice test-driven development also spell it as timely.
Talking about thoroughness. This means that when testing a function, we should consider a happy path as well as a negative scenario. And thus most of the time, we create multiple unit tests to test a function that accepts input parameters. One unit test will test a function under test with a valid input parameter. And another unit test will test the function under the test with invalid input parameters.
If there is a range like MIN and MAX value, then we should create additional unit tests to test for minimum and maximum values as well.
Unit tests should also be timely. It is better to create a unit test at the time you are working on the app feature. This way you will have more confidence that the feature does work as expected and fewer chances, that you will introduce a bug that will be then released to production. So before you promote the code to production it should be covered with unit tests.