In the introduction to unit testing recipe, welearned how to test Dart classes using the test package. In order to testWidget classes, we’ll need a few additional tools provided by theflutter_testpackage, which ships with the Flutter SDK.

The flutter_test package provides the following tools for testing Widgets:

  • The WidgetTester, which allows us to build and interact with Widgets in a test environment.
  • The testWidgets function. This function will automatically create a new WidgetTester for each test case, and is used in place of the normal test function.
  • Finder classes. These allow us to search for Widgets in the test environment.
  • Widget-specific Matcher constants, which help us verify whether a Finder locates a Widget or multiple Widgets in the test environment.If this sounds overwhelming, don’t worry! We’ll see how all of these pieces fittogether throughout this recipe.

Directions

  • Add the flutter_test dependency
  • Create a Widget to test
  • Create a testWidgets test
  • Build the Widget using the WidgetTester
  • Search for our Widget using a Finder
  • Verify our Widget is working using a Matcher

1. Add the flutter_test dependency

Before we can begin writing tests, we’ll need to include the flutter_testdependency in the dev_dependencies section of our pubspec.yaml file. Ifyou create a new Flutter project with the command line tools or code editor,this dependency should already be in place!

  1. dev_dependencies:
  2. flutter_test:
  3. sdk: flutter

2. Create a Widget to test

Next, we’ll need to create a Widget that we can test! For this recipe, we’llcreate a Widget that displays a title and message.

  1. class MyWidget extends StatelessWidget {
  2. final String title;
  3. final String message;
  4. const MyWidget({
  5. Key key,
  6. @required this.title,
  7. @required this.message,
  8. }) : super(key: key);
  9. @override
  10. Widget build(BuildContext context) {
  11. return MaterialApp(
  12. title: 'Flutter Demo',
  13. home: Scaffold(
  14. appBar: AppBar(
  15. title: Text(title),
  16. ),
  17. body: Center(
  18. child: Text(message),
  19. ),
  20. ),
  21. );
  22. }
  23. }

3. Create a testWidgets test

Now that we have a Widget to test, we can begin writing our first test! To getstarted, we’ll use thetestWidgetsfunction provided by the flutter_test package to define a test. ThetestWidgets function will allow us to define a Widget test and will create aWidgetTester for us to work with.

Our test will verify that MyWidget displays a given title and message.

  1. void main() {
  2. // Define a test. The TestWidgets function will also provide a WidgetTester
  3. // for us to work with. The WidgetTester will allow us to build and interact
  4. // with Widgets in the test environment.
  5. testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
  6. // Test code will go here!
  7. });
  8. }

4. Build the Widget using the WidgetTester

Next, we’ll want to build MyWidget inside the test environment. To do so, wecan use thepumpWidgetmethod provided by the WidgetTester. The pumpWidget method will build andrender the Widget we provide.

In this case, we’ll create a MyWidget instance that displays “T” as the titleand “M” as the message.

  1. void main() {
  2. testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
  3. // Create the Widget tell the tester to build it
  4. await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));
  5. });
  6. }

Note

After the initial call to pumpWidget, the WidgetTester provides additionalways to rebuild the same Widget. This is useful if you’re working with aStatefulWidget or animations.

For example, if we tap a button, and this button calls setState, Flutter willnot automatically rebuild your Widget in the test environment. We need to useone of the following methods to ask Flutter to build our Widget once again.

    • tester.pump()
    • Triggers a rebuild of the Widget after a given duration.
    • tester.pumpAndSettle()
    • Repeatedly calls pump with the given duration until there are no longer any frames scheduled. This essentially waits for all animations to complete.These methods provide fine-grained control over the build lifecycle, which isparticularly useful while testing.

5. Search for our Widget using a Finder

Now that we’ve built our Widget in the test environment, we’ll want to searchthrough the Widget tree for the title and message Text Widgets using aFinder. This will allow us to verify that we’re displaying these Widgetscorrectly!

In this case, we’ll use the top-level findmethod provided by the flutter_test package to create our Finders. Since weknow we’re looking for Text widgets, we can use thefind.textmethod.

For more information about Finder classes, please see theFinding Widgets in a Widget Testrecipe.

  1. void main() {
  2. testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
  3. await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));
  4. // Create our Finders
  5. final titleFinder = find.text('T');
  6. final messageFinder = find.text('M');
  7. });
  8. }

6. Verify our Widget is working using a Matcher

Finally, we can verify the title and message Text Widgets appear on screenusing the Matcher constants provided by flutter_test. Matcher classes area core part of the test package, and provide a common way to verify a givenvalue meets our expectations.

In this case, we want to ensure our Widgets appear on screen exactly one time.Therefore, we can use thefindsOneWidgetMatcher.

  1. void main() {
  2. testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
  3. await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));
  4. final titleFinder = find.text('T');
  5. final messageFinder = find.text('M');
  6. // Use the `findsOneWidget` matcher provided by flutter_test to verify our
  7. // Text Widgets appear exactly once in the Widget tree
  8. expect(titleFinder, findsOneWidget);
  9. expect(messageFinder, findsOneWidget);
  10. });
  11. }

Additional Matchers

In addition to findsOneWidget, flutter_test provides additional matchers forcommon cases.

Complete example

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_test/flutter_test.dart';
  3. void main() {
  4. // Define a test. The TestWidgets function will also provide a WidgetTester
  5. // for us to work with. The WidgetTester will allow us to build and interact
  6. // with Widgets in the test environment.
  7. testWidgets('MyWidget has a title and message', (WidgetTester tester) async {
  8. // Create the Widget tell the tester to build it
  9. await tester.pumpWidget(MyWidget(title: 'T', message: 'M'));
  10. // Create our Finders
  11. final titleFinder = find.text('T');
  12. final messageFinder = find.text('M');
  13. // Use the `findsOneWidget` matcher provided by flutter_test to verify our
  14. // Text Widgets appear exactly once in the Widget tree
  15. expect(titleFinder, findsOneWidget);
  16. expect(messageFinder, findsOneWidget);
  17. });
  18. }
  19. class MyWidget extends StatelessWidget {
  20. final String title;
  21. final String message;
  22. const MyWidget({
  23. Key key,
  24. @required this.title,
  25. @required this.message,
  26. }) : super(key: key);
  27. @override
  28. Widget build(BuildContext context) {
  29. return MaterialApp(
  30. title: 'Flutter Demo',
  31. home: Scaffold(
  32. appBar: AppBar(
  33. title: Text(title),
  34. ),
  35. body: Center(
  36. child: Text(message),
  37. ),
  38. ),
  39. );
  40. }
  41. }