Writing Tests For Widgets In Flutter
Writing tests for your software is one of those things every software developer can’t argue against (a few still do though). There are a ton of benefits you get when your software is sufficiently covered by tests, be it unit tests, integration tests, or any other test you know. This truth still holds when it comes to Flutter. When you are building Flutter apps it is very important that you thoroughly test them. In this post I am going to talk briefly about how you can write tests for your widgets in Flutter.
As you know by now, everything is a widget in Flutter. When you create a login screen, it’s just a bunch of widgets that you compose together and add some logic to handle the user’s input. The Flutter SDK provides a WidgetTester
which allows you to build and interact with your widgets in a test environment. This wonderful class is part of the flutter_test
package that comes bundled in the SDK. All you need to do is to add it in your pubspec.yaml
and you’re ready to go:
dev_dependencies:
flutter_test:
sdk: flutter
There are a few steps that you need to follow when you are writing tests for your widgets. I’m going to summarize them but if you need to get more in-depth knowledge I suggest you check out this cookbook from Flutter.
-
Begin your test by calling the
testWidgets()
function. This function allows you to define a widget test and create aWidgetTester
that you will use to interact with your widget under test:testWidgets('What you are testing', (WidgetTester tester) async { // test your widget here });
-
Use the
WidgetTester
to build the widget under test. TheWidgetTester
has a functionpumpWidget()
which builds and renders the widget that you are testing.testWidgets('Should contain welcome message', (WidgetTester tester) async { await tester.pumpWidget(MyApp()); // your widget has been built and rendered. // you may now interact with it. });
-
Use the Finder class which searches your widget tree and returns a node that matches the criteria you provide to get the node you want to test. The
Finder
.flutter_test
provides a top-level functionfind()
that instantiates and uses the CommonFinders which provides a lightweight syntax for accessing commonly usedFinder
s. There are many criteria you can use to search for the node you want. Visit the CommonFinders docs for more information.testWidgets('Should contain welcome message', (WidgetTester tester) async { await tester.pumpWidget(MyApp()); final welcomeTextFinder = find.text('Welcome'); // ... });
-
Next you interact with your widget. This is an optional step as some widgets such as
Text
won’t need you to interact with them. This is is where you use theWidgetTester
to programmatically interact with your widgets using the WidgetController class. TheWidgetController
provides a bunch of actions you may take on your widgets – tapping, dragging and entering text, among others. After you have completed your interactions don’t forget to call the pump() function – very important. Thepump()
function schedules a frame and triggers a rebuild of your widget under test. If your interaction involves animations you might want to call the pumpAndSettle() function which repeatedly callspump()
until all animations are completed.testWidgets('Should enter and display name', (WidgetTester tester) async { await tester.pumpWidget(MyApp()); String name = 'Vince'; // confirm non-existance expect(find.text(name), findsNothing); // use the WidgetController to enter text await tester.enterText(find.byType(TextField), name); // use the WidgetController to tap on a button await tester.tap(find.byType(ElevatedButton)); // Schedule a frame and force rebuild await tester.pump(); // Find and verify (step 5) expect(find.text(name), findsOneWidget); });
-
Use the
Matcher
to verify your widget. Theflutter_test
library provides top-level constants for commonly usedMatcher
s such asfindsNothing
,findsOneWidget
,findsWidgets
. For more information check out the docs.testWidgets('Should contain welcome message', (WidgetTester tester) async { await tester.pumpWidget(MyApp()); final welcomeTextFinder = find.text('Welcome'); expect(welcomeTextFinder, findsOneWidget); }); ``` And you are done. These are the 5 steps (well, my 5 steps) that you take when you are writing tests for your widgets. I will need to stress this important point again -- don't forget to `pump()` after you've interacted with your widget(s). Trust me, it will save you a lot of headaches.
Once again, thank you for taking your time to read and I hope you have learnt something. Personally, writing this post gave me the opportunity to dig deeper into the documentation to uncover more things I didn’t know which is great. Till next time, have a great one!
Comments