We cover a basic example of how unit tests in a Flutter project can look like. Explained code samples are included.
Testing is an essential task in every software project (or at least it should be one). This time we are focussing on the basics of unit testing in a simple Flutter application.
I assume you are familiar with the concept of unit testing, the why and the what for. So we will focus on writing the test cases and how to structure them. This is mainly for people who didn’t write many or any tests for a Flutter application at all. It will give you a compact overview to start with. A link to the used sources is provided at the end.
This is the class we are going to write unit tests for.
This class manipulates a counter value when the methods are called. There are some conditions to be considered and a maximum value that the counter can’t exceed.
A little bit of setup
Let’s assume your class to test is called
counter_service.dart and sits under the
lib folder in your project. Your matching test class will be called
counter_service_test.dart and will be in the
test folder structure. If you follow this convention the Flutter test runner can find and execute your tests. It can be triggered by the command
flutter test while you are in the root folder of your project.
First, we test the
inc() method. What we want to achieve is that every code branch is executed. This means we can cover this by writing three tests.
- Validate that the counter is increased if there is no
- Validate that the counter is increased if
maxCounterValueis not reached yet.
- Validate that the counter is not increased if
Every test creates an instance of the class to test (System Under Test, that’s why I call the variable
_sut), manipulates data, and checks the results. Verification of results is done by the expect function, which takes the actual value and a Matcher to evaluate the outcome. Here are some examples of how to work with matchers.
Testing the dec() method
This method is a bit simpler than the one before, we just need 2 tests to cover all possible outcomes.
- Validate that the counter can be decreased
- Validate that the counter cannot be decreased below zero
As you can see every test follows the Arrange-Act-Assert pattern. It helps to structure the tests and is one of the most common strategies to write test code.
- Line 2–4 is the “Arrange” part where data is set up.
- Line 6 is the “Act” part where code is executed that we want to test.
- Line 7 is the “Assert” part where we check if the executed code did what it was expected to do.
To execute the tests run the command
flutter test. The test results will be printed to the console window. Many IDEs or editors support running tests as well.
Remarks on unit testing
- Use good test descriptions
If your tests increase in numbers you will most likely not remember what each test does. With a good description, you know more about the context and spend less time finding the problem.
- Keep your tests short
One should always see what a test does at first glance. If scrolling is necessary or many method calls are involved, it gets messy.
- Make your test independent of other tests
A test should always work in the same way. So make sure to set up data properly (and even reset them after the test if needed).
- Write tests while writing code
Don’t skip the tests when you start. It is pretty certain that once you start writing tests you won’t cover everything as you would have if you had started writing them from the beginning.
You can find the source code on GitHub.