FIRST is an acronym representing a set of rules that can help us write better tests. First principles are like SOLID but for tests, you can applay it even for your architecture tests. I have always believed that this concept should be more popular because of how helpful it may be.
That is why today I want to give you a more detailed explanation of these principles. Besides, I want to show why it will be beneficial for you and your team to stick to these principles.
What is FIRST?
It is an acronym representing a set of rules with similar purpose as SOLID. However, FIRST is applied to tests instead of object-oriented programming.
Proposed by Robert. C. Martin in his book “Clean Code” to make writing tests easier. By giving us a set of rules, which we can follow when writing our tests. Aimed to increase the quality of unit tests but, in my opinion, can be considered useful for any type of tests.
Let’s explain it letter by letter.
FIRST Principles Breakdown
F—Fast
Tests should be as fast as possible. The faster the tests, the more willing we are to run them. By extension, the more profitable they are.
Execution time is especially important for unit tests. They should be executed by every member of your team at least one time per each task they do. For example, before committing changes to check if nothing got unintentionally broken down.
Tests are also the backbone, at least they should be, of all CI processes. Nobody wants to wait for a long time for CI to finish verifying our pull request.
In my opinion the execution of unit tests should not take more than a few seconds. Even for more complex systems with many unit tests. An execution time of 10 or 15 minutes for unit tests is something absolutely wrong. I bet there are some serious problems with your production and/or test code.
On the other hand, more complex algorithms, like graphs, especially on large data sets, are a completely understandable exception to this rule. Also for very huge sets of unit tests—like hundreds of thousands or millions—execution time should not be a primary concern.
I think for you and your team to stop from time to time and think how you can make your tests faster without losing the guarantees they give.
I—Isolated
Tests should not depend on one another—it is as simple as that. Each test should do any setup required to be executed correctly and clear any context, which was used. It is a very good and useful rule for all types of tests and probably the most crucial one out of all five. There should not be a case when tests results depend on the test order.
Its breaking may result in many false positive or false negative tests. It is an easy way to make your tests lose their worth and do more harm than good.
The rule is quite easy to follow for unit tests but harder for integration or end-to-end tests. Here concepts like fixtures may come in handy.
R—Repeatable
Tests should be able to run in every environment. Their results should also be the same on each of them.
Tests whose results differ from environment to environment cannot bring any good to your team. Such tests can lead to any number of curious or even funny situations, or tragic if you prefer.
I believe that no one wants to see that during pre-prod or prod deployment all tests included in your CD process failed. While a few stages earlier during deployment to dev every one of them passed and everything was green. Especially, when it is Friday and all you want is to go home after pressing the “deploy” button.
S—Self-Validating
Each of your tests should be able to auto-detect if it passed or not. It should have clearly defined assertions and reasons to fail or pass.
If you are forced to do any type of manual comparisons to check if a test passed or failed then it means there is a problem with your tests’ setup. In such a case a number of failed or passed tests may depend on various reasons and thus be completely random. For a start the person checking the test didn’t notice a typo somewhere. Not to mention all the costs related to such an approach.
In the case of performance or security tests you can automate them to be able to easily verify if your application meets requirements or not.
T—Timely/Thorough
Original explanation of this letter is Timely. The tests should be written at the correct time. It means while writing a production code.
Such an attitude should force you to write a cleaner and more testable code. TDD enthusiasts claim that writing tests after your production code can lead to a more dirty and less testable code. It may bring benefits for the unit tests but will not be so good for other test types.
That is one of the reasons why I, prefer the second explanation of this letter—Thorough. When we test a method, we should cover not only the execution of happy paths. We should also cover possible errors and negative paths.
It helps make our tests more meaningful and complete. It will give us a better understanding of more complex parts of our code. Naturally, there is no big deal in following both these two explanations together and combining their advantages.
With all principles explained let’s dive into when we can bend them.
When FIRST Principles Can Bend
Scenario | Principle to bend | Why it is OK |
---|---|---|
Load Tests | Isolated | Complex bugs may show up under very specific conditions |
Security tests | Isolated | It require mixing different steps to test some vulnerabilities |
Complex algorithms | Fast | We should focus more on correctness and behavior then how fast it executes |
Chaos Engineering | Repeatable | These type of tests in not repeatable by default |
E2E Tests | Fast | Try to optimize them for speed but focus more on Thorough |
Summing up
Every programming paradigm has its own unique sets of rules. FIRST is their counterpart, but you can follow it in any language, or paradigm. At least as long as you are writing tests.
If you follow all the principles then, your tests will be:
- Faster — runs finish quickly.
- More Thorough — edge-cases and unhappy paths are covered.
- More Trusted — failures signal real problems, not flaky tests.
- Cheaper to Maintain — clear structure and isolation.
- Easier to Debug — failing tests point straight to the problem.
Let’s MTGA — make tests great again, or just keep them great. Thank you for your time.