Integration Testing in Flutter: Getting Started
Learn how to test UI widgets along with the backend services in your Flutter project using Integration Testing. By Monikinderjit Singh.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Integration Testing in Flutter: Getting Started
30 mins
- Getting Started
- Setting Up a Firebase Project
- Setting Up The Android Project
- Setting Up The iOS Project
- Setting Up Authentication and Firestore
- Exploring the Starter Project
- Testing in Flutter
- Comparing Types of Testing
- Examining Unit Testing
- Examining Widget Testing
- Examining Integration Testing
- Setting Up the Project for Testing
- Adding Dependencies
- Creating a Test Directory
- Writing Your First Test
- Diving into LiveTestWidgetsFlutterBinding
- Grouping Tests
- Testing Feature One: Authentication
- Understanding pumpWidget and pumpAndSettle
- Diving Into Widget Keys
- Adding Fake Delays
- Understanding expect()
- Running the Test
- Testing Feature Two: Modifying Ideas
- Inserting New Ideas in a List
- Deleting an Idea
- Where to Go From Here?
Setting Up the Project for Testing
To create integration tests for Flutter, you’ll use the integration_test
package. In the past, you would have used flutter_driver
, but Flutter discontinued it for the following reasons:
- Difficulty in catching exceptions.
- Hard interaction with the app components like
showBottomSheet
. - Poor readability of the API.
- Difficulty in verifying the state of the app.
The integration_test
package solves these issues.
Now it’s time to learn how to configure your project to write integration tests. First, you’ll add all the required dependencies.
Adding Dependencies
The Flutter SDK includes integration_test
package, so you don’t need to copy it from the Pub.dev website. Instead, you just need to add integration_test
to your pubspec.yaml.
Open pubspec.yaml and replace # TODO: add integration test dependency here
with:
integration_test:
sdk: flutter
Don’t forget to run flutter pub get
in the terminal.
Now, you’ll create the test directory.
Creating a Test Directory
In the project’s root, create a folder named integration_test. This folder will act as a directory for all of the project’s integration tests.
Inside the integration_test folder, create a dart file named app_test.dart. This file will include your integration tests.
Now, it’s time to start writing integration tests.
Writing Your First Test
In app_test.dart, insert:
void main() {
}
This function is the first called when running the tests. You’ll write all tests inside this function.
At the top of app_test.dart, insert:
import 'package:integration_test/integration_test.dart';
Here, you import the integration_test
package making it ready to use in the file.
Now, inside main()
, add:
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
Here, ensureInitialized()
verifies the integration test driver’s initialization. It also reinitializes the driver if it isn’t initialized.
Next, you’ll learn how to use the LiveTestWidgetsFlutterBinding
method.
Diving into LiveTestWidgetsFlutterBinding
Insert the following code at the top of app_test.dart:
import 'package:flutter_test/flutter_test.dart';
This code includes the flutter_test
package required for configuring the test.
Then, add this code block below the binding
variable you defined before:
if (binding is LiveTestWidgetsFlutterBinding) {
binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
}
LiveTestWidgetsFlutterBindingFramePolicy
defines how LiveTestWidgetsFlutterBinding
should paint frames. fullyLive
is a policy used to show every frame requested by the framework and is best suited for heavy animations.
Checkout the official docs for other policies that might make more sense for your app.
Next, you’ll work on grouping tests.
Grouping Tests
At the last line inside main()
, insert:
group('end-to-end test', () {
//TODO: add random email var here
//TODO: add test 1 here
//TODO: add test 2 here
});
The group()
method groups and runs many tests. You include it here since you’ll run multiple tests in the app. It has the following arguments:
- description: A description of the test group.
- void Function() body: A function defining what tests to run.
-
skip: An optional argument used to skip the test group. Since it’s dynamic, if the value is
String
rather thantrue
, it’ll print the value ofString
when skipping the test.
Now you have all the skills you need to create your first test!
Testing Feature One: Authentication
Most apps start with an authentication screen, and this project is no different. Therefore, you’ll start by writing an authentication test first.
Write the following code in place of //TODO: add random email var here
:
final timeBasedEmail = DateTime.now().microsecondsSinceEpoch.toString() + '@test.com';
This code creates a time-based email address.
Now, replace //TODO: add test 1 here
with:
testWidgets('Authentication Testing', (WidgetTester tester) async {
//TODO: add Firebase Initialization Here
});
testWidgets()
lets you define tests for widgets and takes two required parameters:
- description: Defines what the test is about.
-
callback function: A function that executes during the test. It takes a
WidgetTester
object as a parameter. ThisWidgetTester
object interacts with the widgets and the test environment.
The callback function is asynchronous because your test will interact with real-world APIs.
testWidgets()
also has some optional parameters like skip
and timeout
.
timeout
is the maximum time required to run the test. After that time, the test will fail automatically. It’s ten minutes by default.
Insert the following lines at the top of app_test.dart:
import 'package:firebase_core/firebase_core.dart';
This code lets you initialize your Firebase app during the test.
Replace //TODO: add Firebase Initialization Here
with:
await Firebase.initializeApp();
This code ensures your app is ready to use Firebase services.
Now, you’ll work with pumpWidget
and pumpAndSettle
.
Understanding pumpWidget and pumpAndSettle
At the top of app_test.dart, add:
//1
import 'package:ideast/main.dart';
//2
import 'package:flutter/material.dart';
Here’s a code breakdown:
- Imports main.dart to get access to
MyApp()
. - Imports material.dart to access Flutter widgets.
Now, below the code you just added, add:
await Firebase.initializeApp(); // previous code
await tester.pumpWidget(MyApp());
await tester.pumpAndSettle();
//TODO: Add here
Here’s an explanation of the code:
duration
is the most suitable option when you know how many frames will render, such as navigation without animations.
-
pumpWidget()
renders the UI of the provided widget. Here you passMyApp()
as the rendering widget.PumpWidget
also takesduration
as a parameter, which will shift the fake clock by the specified duration to help you avoid excessive frame rates.duration
is the most suitable option when you know how many frames will render, such as navigation without animations. -
pumpAndSettle()
repeatedly callspump
for a given duration until there are no frames to settle, which is usually required when you have some animations.pumpAndSettle
is called afterpumpWidget()
because you want to wait for the navigation animations to complete.
Replace //TODO: Add code here
with:
await tester.tap(find.byType(TextButton));
//TODO: Add code here
tap()
is a method in WidgetTester that lets you tap the centre of the widget. This requires a Finder
to tell the framework to tap it.
The byType
property defines the type of widget. TextButton
and ElevatedButton
are acceptable but not abstract classes such as StatefulWidget
.
Replace //TODO: Add code here
with:
//1
tester.printToConsole('SignUp screen opens');
//2
await tester.pumpAndSettle();
//3
await tester.enterText(find.byKey(const ValueKey('emailSignUpField')), timeBasedEmail);
Here’s a code breakdown:
-
printToConosle
prints statements during the test. Including a few descriptive statements provides a sense comfort that the tests are running. - Waits for all animations to settle down.
- In the text field, you can enter text using the tester’s
enterText
property. There are two parameters: aFinder
and aString
.String
enters this text into the Text field.byKey
finds widgets using their widget keys.
In the next section, you’ll learn how to interact with the widgets using keys.