Purpose of the article: To better understand flutter widget testing.
Intended Audience: Intermediate
Tools and Technology: Android Studio, Flutter SDK, Dart, XCode, flutter_test library.
Keywords: Flutter, Testing, Widget Test, Dart
Introduction to widget testing
The widget test is testing UI components, so it is also called Component Testing. It is used to test a single widget. The main goal of widget testing is to check whether the widget works as expected. A user can write test cases for every widget present in the project.
Dependency:
We need to add flutter_test dependency in pubspec.yaml file under the dev_dependencies section.
dev_dependencies:
flutter_test:
sdk: flutter
We can add dependency by typing the below command in the terminal of your project root directory.
flutter pub add flutter_test
You can ignore this step if flutter_test already exists in the pubspec.yaml file.
Syntax:
testWidgets (description, callback);
description – String, which describes the test case.
Callback – function, callback function for the widget test.
Example:
testWidgets (‘Flutter Widget Test’, (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: Text(‘Hello’)));
Finder helloText = find.byType(Text);
expect(helloText, findsOneWidget);
});
The above example demonstrates widget testing. The test widget method takes two parameters: one is the test description, and the other is the callback method. The ‘pumpWidget’ will build and render the widget. Finder will search for the widget-by-widget type, and the expect method will verify the widget with matcher.
Create new widget for testing
The below widget displays title and message.
class MyWidget extends StatelessWidget {
const MyWidget ({
Key,
@required this.title,
@required this.message,
}) : super(key: key);
final String title;
final String message;
@override
Widget build (BuildContext context) {
return MaterialApp (
title: ‘Flutter Demo’,
home: Scaffold (
appBar: AppBar(title: Text(title)),
body: Center (child: Text(message)),
),
);
}
}
Add flutter_test dependency
Add flutter_test dependency in pubspec.yaml file as shown below, if not exist.
dev_dependencies:
flutter_test:
sdk: flutter
Create a new test file
Create a new test file under the test folder for testing ‘MyWidget’; ‘testWidgets’ method allows to define widget test and creates ‘WidgetTester’ class to work with tests. The below test verifies that the ‘MyWidget’ displays the correct title and message.
void main () {
testWidgets (‘MyWidget has a title and message’, (WidgetTester tester) async {
// Test code goes here.
});
}
Build Widget using WidgetTester
In the next step, one needs to build ‘MyWidget’ in a test environment by using the method ‘pumpWidget ()’, which was provided by ‘WidgetTester’ class. The ‘pumpWidget’ method builds and renders the widget.
The below example demonstrates and renders the ‘MyWidget’ with the help of ‘pumpWidget’ method. The ‘MyWidget’ displays “Hi” as title and “Hello” as message.
void main () {
testWidgets (‘MyWidget has a title and message’, (WidgetTester tester) async {
await tester.pumpWidget(
const MyWidget(title: ‘Hi’, message: ‘Hello’));
});
}
pump () methods
To rebuild the same widgets, WidgetTester provides the pump methods.
tester.pump(Duration duration): Schedules the frame and triggers a rebuild of the widget.
tester.pumpAndSettle(): frequently calls pump () method with the given course of time until there are no scheduled frames. pumpAndSettle () method waits for all animations to complete.
Search widget using a Finder
Flutter_test dependency provides a Finder class to search the widgets for widget tree based on a test, byType, byKey, and byToolTip, etc.
The below example demonstrates the search widget by the text.
void main() {
testWidgets(‘MyWidget has a title and message’, (WidgetTester tester) async {
await tester.pumpWidget(const MyWidget(title: ‘Hi’, message: ‘Hello’));
final titleFinder = find.text(‘Hi’);
final messageFinder = find.text(‘Hello’);
});
}
Verify widget using Matchers
To verify the widgets, we can use the Matcher constants provided by flutter_test dependency.
Here we need to verify the title and message widgets with the matchers. To ensure that the widgets appear only once on the screen, we use Matcher constant ‘findsOneWidget’.
void main() {
testWidgets(‘MyWidget has a title and message’, (WidgetTester tester) async {
await tester.pumpWidget(const MyWidget(title: ‘Hi’, message: ‘Hello’));
final titleFinder = find.text(‘Hi’);
final messageFinder = find.text(‘Hello’);
expect(titleFinder, findsOneWidget);
expect(messageFinder, findsOneWidget);
});
}
Additional Matchers
Other than findsOneWidget, flutter_test provides the following Matcher constants.
findsNothing: verifies that there are no widgets found on the screen.
findsWidgets: verifies that there is at least one widget found on the screen.
findsNWidgets: verifies that there is N number of widgets found.
matchesGoldenFile: verifies that the widget matches the particular bitmap image.
Find a Text widget
In widget testing, to find widgets that contain specific text by using ‘find.text()’ method. The below example demonstrates the find widget by containing text.
testWidgets(‘finds a Text widget’, (WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(
home: Scaffold(
body: Text(‘Hello’),
),
));
expect(find.text(‘Hello’), findsOneWidget);
});
Find a widget with a specific Key
In some cases, to find widgets by their key, we need to use the method ‘find.byKey()’. For this, we need to provide a key to each widget.
testWidgets(‘finds a widget using a Key’, (WidgetTester tester) async{
const testKey = Key(‘K’);
await tester.pumpWidget(MaterialApp(key: testKey,
home: Container()));
expect(find.byKey(testKey), findsOneWidget);
});
Enter text in the text field
‘WidgetTester’ provides enterText() method to enter the text into text form field in test environment.
testWidgets(‘Add and remove a todo’, (WidgetTester tester) async{
final controller = TextEditingController();
await tester.pumpWidget(MaterialApp(
home: TextField(controller: controller)));
await tester.enterText(find.byType(TextField), ‘hi’);
});
Tapping a button:
WidgetTester class provides a method to tap on the button. The following code demonstrates the tap() functionality in a test environment.
testWidgets(‘Add and remove a todo’, (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: TextButton(child: Text(‘TextButton’))));
await tester.tap(find.byType(TextButton));
await tester.pump();
expect(find.text(‘hi’), findsOneWidget);
});
References:
- An introduction to widget testing: https://flutter.dev/docs/cookbook/testing/widget/introduction
- Widget Testing with Flutter: https://medium.com/flutterdevs/widget-testing-with-flutter-59cbc020156f