Flutter is a cross-platform application development framework. But no application is really complete without a comprehensive testing suite. In this article, I will tell you the basic principles of both unit- and widget-testing Flutter apps.
Why test?
Testing is vital when developing any serious software project. With a good test suite in place, you can make changes to any part of the code without worrying if that is going to break anything. With tests, you can run them any time and get an answer within minutes if the system is working correctly. If you, as a developer do not test your code, how can you expect your clients to test it?
Unit tests
We are going to begin with unit tests, them being the simplest ones. It is important to note that Flutter clearly separates unit tests from widget tests. Where widget tests assert on the widget tree and test the UI, unit tests are not supposed to render the UI and test purely the business logic.
To start testing your classes, you need to add the test
dependency to your pubspec.yaml
:
dev_dependencies:
test: 1.14.4
Now let’s create a simple model class that we will test:
This is a simple class with 2 properties and a custom getter. Now, create a file PersonTest.dart
in the test
folder:
This is the simplest test example ever. Firstly, on lines 1-2 we import the test
module and the Person
class that we are about to test. Then, in the main
method, we define a test using the test
function. It will accept the name of the test and a callback function, which executes the test itself. In it, we create the person
model and expect
that person.name
will be 'John Smith'
.
test
package exposes a number of assertion functions that can be used with expect
, like:
expect(person, isA(Person))
expect(person, isA(String))
Here are some of the common assertions from the matcher
module (built into test
):
isEmpty
isList
isFalse
/isTrue
isNaN
isNull
throwsException
Now, if you try running the tests (you can do it with Android Studio, Run -> Run tests), you will see that this test passes. Good job!
Widget testing
Unit tests are pretty simple: perform actions, assert on the results. When UI comes into play, everything becomes complicated. Let’s again write a simplest example possible: a widget that displays your name. We will make sure it renders correctly and shows your name on the screen.
Firstly, let’s create the widget. Add the following to your main.dart
file:
Should be nothing surprising here, just a MyApp
stateless widget that requires a named parameter name
. After doing this, add the testing code in test/main_test.dart
:
You can see that instead of importing the test
package, we have to import the flutter_test
one. To make it work, make sure you have this in your pubspec.yaml
:
dev_dependencies:
flutter_test:
sdk: flutter
Now, on line 5 note how we use the testWidgets
function instead of the test
. It injects the WidgetTester
object for you that contains some UI fixtures required for rendering. It is also asynchronous, hence the async
keyword for the callback function.
On line 6 we render the UI with the pumpWidget
function. It behaves exactly as you would expect, you simply pass a widget to it. After that, we create a finder, which is used to find widgets in the UI tree. We want to find the text box with the name specified. Lastly, on line 10 we expect
that nameFinder
will findsOneWidget
. For other cases, you can also assert with findsNothing
, findsWidgets
(one or more) or findsNWidgets
(exactly n
widgets).
Closing notes
In this article I explained the foundations of testing Flutter apps, thank you for reading. In the following posts I will go into advanced testing and mocking in Flutter.