Software testing undoubtedly brings many advantages, increasing trust in the created project and also allowing you to indicate errors during mobile app development. If you want to develop a high-quality product, you definitely should test it. It is no different in the case of building commercial apps in the Flutter framework. As you probably know, in Flutter app development we have a few kinds of testing categories:
- Unit test
- Widget test
- Integration test
This blog post will help you become a better tester by providing in-depth information on each testing subcategory and giving four recommendations for improving your testing skills.
Generally, unit tests written in Dart are no different from tests in other programming languages. This type of app testing is the easiest and fastest to implement, so it is worth starting with it. Additionally, they have the fastest startup time, so the frequency of running them does not waste time.
Take, for example, a simple function that checks whether a user is of age. Give it a single parameter of type INT and the result will be either TRUE or FALSE depending on the given parameter.
To start the testing process, we create a new file, and in the main function, we call the test method, which is available thanks to the flutter_test package – we need to add a dependency to the project. By default, with the creation of a new project, this package is already available.
The test method takes a parameter of type String – the name of the test and the function that is to perform the test. If there is an error and we do not get the expected result, the test will not be passed. Otherwise, the test will be passed.
Before you start writing a test widget, you should prepare a simple UI. We add a simple widget, which takes care of accepting the age of the user and when the button is pressed, the isAdult function is called. Below, we can see what the code looks like. This is a StatefulWidget with two fields: userAge and result. It stores the validation result.
Our tests will check the following behaviors:
- Is the button visible?
- Is the error hidden by default?
Here we have a test that checks whether the widgets in the widget’s tree are visible or not. First, call the function testWidget, which takes two parameters – the name of the test as a String and a function that additionally has the variable tester.
First, we need to build a tree of widgets, which is what the pumpWidget function is used for. The next step is to search this tree and if we find the expected widget, we perform the appropriate action.
Widget testing in Flutter also allows us to check more comprehensive measures. We will check if an error is displayed when a minor age is entered.
In the second case, after creating the widget tree, we find a TextFormField, and fill it with the appropriate data – that is, we enter the age for the minor and then perform a button-press action which will call the isAdult function and consequently show (or not) an error. In this case, the error was shown which passes our test.
The methods used in the tests are provided by the flutter_test package. These are extremely helpful functions and comparators, thanks to which we can easily verify whether there is a widget with a given text in the widget tree or simulate specific actions like typing a particular text into a TextFormField or pressing a button.
The last and most complicated form of testing Flutter apps is integration testing. They involve running the application on a real device or emulator and simulating user behavior, e.g.: triggering a screen touch event.
The integration tests discussed here check applications holistically; their runtime is far the longest because the application must be “clicked through” many times during such a test. So it is good to focus on the most important aspects of the application.
In our case, we will use the base code that Flutter generates for us when we create a new application and create an integration test that will build the application, execute the press action on the FloatingActionButton and verify that the increment function works properly.
First, we need to add integration_test to our pubspec.yaml file. The next step is to create the integration_test directory and then create the file. After writing the test, execute it with the following command:
flutter test integration_test/app_test.dart
In this case, the test file is located in a separate integration_test directory. First, we make sure that everything is properly initialized and then we execute the test. In the first step, the application is built and then we check that the default state of the counter is 0. Next, we find a button to perform a press action on our FloatingActionButton, which will call the increment function. We wait for one frame and check the result, which should be 1. This is how our test was correctly performed.
Next steps for testing Flutter applications
To kick your testing skills to the next level in Flutter, try to start testing based on a mature state management method. One of the most effective tools for this is the BloC package, which provides dedicated testing tools.
If you have some experience in testing, I have a few tips to make your Flutter testing environment better, faster, and more efficient.
Try to achieve 100% test coverage
It is very important to cover our code with 100% tests. It means that all logical behavior has its test. Of course, that situation doesn’t mean that our code is 100% bug-free or that everything works as expected.
One of the biggest benefits of covering code with 100% tests is that our future code will also have tests and will not break the tests we have already written. If our codebase has 100% test coverage it forces us to write tests for future code.
Mockito framework is a library for testing purposes to mock class behaviors. Why should you use that Flutter package? Imagine that you have communication with API and sometimes a single call takes longer than usual. We don’t want to make that call and wait for the result because we test behavior and result in our test so we can mock it using Mockito. For example:
when(_apiClient.fetchListOfStudents()).thenReturn([‘Thomas’, ‘Ann’, ‘Joe’, ‘Martha’]); when(_fileManager.getFile()).thenThrow(Exception());
As you can see, Mockito is very useful when you want to mock the behavior of any class. It is the best way to test all cases with all the control.
Manual app testing
Automated tests are not always the best choice, especially if we are dealing with very customized solutions. Sometimes it is better to engage an additional person to test the functionality “manually” which often allows us to find the bug in question faster. In addition, if such tests are carried out by several different people in different ways, we can be almost sure that the code we wrote works correctly.
Unfortunately, manual tests can consume more resources which implies that they should be used accordingly. Sometimes manual tests can take days or even weeks to complete, which tells us only one thing: it’s worth analyzing what type of testing is most appropriate at the moment of the development of the software being produced.
Try not to bypass automated tests
The process of running tests is, of course, the extra time you should spend on Flutter app development. Such code should be maintained and always keep tests in mind for new functionality. Consider that a good investment of resources in tests will increase the confidence of our code and often eliminate bugs.
Therefore, it is worth writing tests in parallel with code development. It is not worth putting it off until the end, because we will lose context. Remember that if you write tests more often, you will develop a good habit as a great Flutter developer that all the world needs 🙂
Do you want to find out more about Flutter app testing?
Developing high-quality code with the right testing
Testing and verification of the application’s functionality prior to release is an essential part of any software development process. In this way, we know that the Flutter application is of high quality and that the user experience is positive. Unit tests, widget tests, and integration tests – all of these approaches have been described in greater detail and I hope that the given tips will be a step toward becoming a better developer. For further Flutter insights, I can recommend you the blog post by Michał on securing Flutter apps.