Embrace FIRST Principles for Reliable Unit Tests

In any application, which solves real-world problems, having problems in its unit tests – is the least desirable thing. Good written tests are assets while poorly written tests are burdens to our applications. In this tutorial, we will learn about the FIRST principles that can help make our tests shine and ensure that they pay off more than they cost.

1. The F.I.R.S.T. Principles

Acronym FIRST stand for below test features:

  • [F]ast
  • [I]solated
  • [R]epeatable
  • [S]elf-validating
  • [T]imely

If we follow these five principles in writing our unit tests, we will have more robust unit tests and thus more stable application code.

Let’s learn about these FIRST principles – in detail.

1.1. Fast

Unit tests should be fast otherwise they will slow down your development/deployment time and take longer to pass or fail. Typically on a sufficiently large system, there will be a few thousand unit tests – let’s say 2000 unit tests. If the average unit test takes 200 milliseconds to run (which shall be considered fast), then it will take 6.5 minutes to run the complete suite.

The 6.5 minutes doesn’t seem long at this stage, but imagine if you run them on your development machine multiple times a day which will eat up a good amount of productive time. And imagine when the count of these tests increases when new functionalities are added to the application, it will further increase the test execution time.

The value of your suite of unit tests diminishes as their ability to provide continual, comprehensive, and fast feedback about the health of your system also diminishes.

One of the major causes of slow tests – is a dependency that must handle external evil necessities such as databases, files, and network calls. They take thousands of milliseconds. So to make the suite fast, you must avoid creating these dependencies by using mock testing.

1.2. Isolated

Never ever write tests that depend on other test cases. No matter how carefully you design them, there will always be the possibility of false alarms. To worsen the situation, you may spend more time figuring out which test in the chain has caused the failure.

In the best-case scenario, we should be able to run the tests at any time, in any order.

By making independent tests, it is easy to keep your tests focused only on a small amount of behavior. When this test fails, you know exactly what has gone wrong and where. No need to debug the code itself.

The Single Responsibility Principle (SRP) of SOLID Class-Design Principles​ says that classes should be small and single-purpose. This can be applied to your tests as well. If one of your test methods can break for multiple reasons, consider splitting it into separate tests.

1.3. Repeatable

A repeatable test is one that produces the same results each time you run it. To accomplish repeatable tests, you must isolate them from anything in the external environment, not under your direct control. In these cases, feel free to use mock objects. They have been designed for this very purpose.

Occasionally, you must interact directly with an external environmental influence such as a database. You’ll want to set up a private sandbox to avoid conflicts with other developers whose tests concurrently alter the database. In this situation, you may use in-memory databases.

If tests are not repeatable then you will surely get some bogus test results, and you can’t afford to waste time chasing down phantom problems.

1.4. Self-validating

Tests must be self-validating means – each test must be able to determine whether the output is expected or not. It must determine whether it is failed or passed. There must be no manual interpretation of results.

Manually verifying the results of tests is a time-consuming process that can also introduce more risk.

Make sure you don’t do anything silly, such as designing a test to require manual arrangement steps before you can run it. You must automate any setup your test requires – even do not rely on the existence of a database and pre-cooked data.

Create an in-memory database, create schema and put dummy data and then test the code. In this way, you can run this test N number of times without fearing any external factor affecting test execution and its result.

1.5. Timely

Practically, You can write unit tests at any time. You can wait until the code is production-ready or you are better off focusing on writing unit tests in a timely fashion.

As a suggestion, you should have guidelines or strict rules around unit testing. You can use review processes or even automated tools to reject code without sufficient tests.

The more you unit-test, the more you’ll find that it pays to write smaller chunks of code before tackling a corresponding unit test. First, it’ll be easier to write the test, and second, the test will pay off immediately as you flesh out the rest of the behaviors in the surrounding code.

2. Bonus Tips

If you use Eclipse or IntelliJ IDEA, consider incorporating a tool like Infinitest. As you make changes to your system, Infinitest identifies and runs (in the background) any tests that are potentially impacted.

On an even larger scale, you can use a continuous integration (CI) tool such as Jenkins or TeamCity. A CI tool watches your source repository and starts a build/test process when it recognizes changes.

Drop me your queries related to FIRST Principles in the comments section.

Happy Learning !!

Reference: This article refers and uses few examples as given in “Pragmatic Unit Testing in Java 8 with JUnit” by Andy Hunt; Jeff Langr; Dave Thomas.

Comments

Subscribe
Notify of
guest
2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode