Testing gestures using Flutter driver

Darshan Kawar
Flutter Community
Published in
7 min readMay 30, 2019

Most of the apps we use have gestures to interact with features. As an example, swipe and tap are the most common gesture users use to perform an action.

Let’s take a look at their usage in an app.

Swipe: The apps which have How it Works feature, use a swipe gesture to go from one screen to another. User can swipe the screen left to right and right to left.

https://pusher.com/tutorials/onboarding-flutter-part-1

Tap: Very first and most used gesture in any app. Whenever you want to interact with the widgets (ex: TextField), you first tap on it and then enter data. Some of the tap’s variants are double tap and long press.

I wrote on Flutter driver earlier in which I gave an overview of what Flutter Driver is and some of the methods it provides to test basic user interactions.

In today’s article, I’ll discuss on flutter driver tests that can be helpful while writing end to end integration tests to validate swipe and long press gestures for below widgets.

  1. App drawer

This widget is part of Scaffold and is the first icon (hamburger icon) that appears at the top left corner of the app bar. When we tap on the app drawer, it slides in onto the screen and displays content. The drawer doesn’t close/slide out by itself unless we tap outside of the drawer or use Navigator to close it. Navigator.of(context).pop();

So, instead of using Navigator, we’ll use this as an opportunity to write a test that will validate the drawer’s slide in and slide out behaviour and make sure it works well.

Approach to identify app drawer

Flutter driver currently doesn’t support widgets identified by icons, so we have to rely on another form of identification to uniquely distinguish with other widgets. To do that, if we long press on the hamburger icon, it gives us a tooltip Open navigation menu which we will use as aSerializableFinder to help the driver locate it.

Use case:

We’ll test this flow for app drawer:

Locate drawer -> open it -> close drawer

Note: I have skipped the flutter driver configuration and folder structure. You can check my previous article and also the flutter documentation here.

Let’s write our test for the flow we discussed above. In, app_test.dart, we’ll start by defining the required finder and then tell the driver to tap on it.

test('drawer navigation and close', () async {
final SerializableFinder locateDrawer = find.byTooltip('Open navigation menu');

// Open the drawer
await driver.tap(locateDrawer);

At this point, let’s run our test to see if the drawer opens properly.

drawer opens

Now, in order to test slide out / closing of the drawer, we’ll make use of driver.scroll() method. First, let’s take a look inside this method.

scroll() tells the driver to perform scroll action on the finder and takes 4 arguments.

finder: identifiable element for the driver to perform an action on.

dx (horizontal) and dy (vertical) : total offset for entire scrolling action depending on scroll direction. For horizontal scroll, we use dx and dy for vertical scroll.

duration: to specify the length of the action to be performed on the finder.

In our case, the driver.scroll() implementation will look like below:

await driver.scroll(locateDrawer, -300, 0, Duration(milliseconds: 500));

locateDrawer is the finder we pass for the driver to know on which element it need to perform scroll action.

  • Since the drawer is open at the moment, we need to swipe it to the left horizontally to close. Hence, we pass -300 as the dx double value.

The reason I passed -300 is because, the drawer’s default width is set to 304. So, in order to close it, we need to pass -300 to bring it back to original position.

Source : https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/material/drawer.dart#L41

  • 500 is the duration we pass for the driver to close/slide out the drawer.

Now, let’s run our test and see the result:

drawer slide out via test

2. PageView

This widget is used to implement scrollable pages on the screen. An introduction of an app showcasing what the app is all about is the best example of a feature that can be implemented using pageview. First-time users of the app can swipe the screen from left to right and vice versa to know what the app does and how it functions.

Use case:

Let’s consider we’ve below implementation using pageview. It’s a simple flow in which swiping the pages shows corresponding text. User can start by swiping to the right to last page and then can swipe to the left to go back to the first page.

We’ll test this flow:

Locate pageview -> swipe to right -> validate Second text is displayed -> swipe to right again -> validate Third text is displayed -> swipe to left -> swipe to left again to go to first page

Let’s write a test for this flow to go from the first page to last. We’ll use scroll() to tell the driver to swipe the finder element to the right and validate the text displayed on each page along the way.

test('test pageview horizontal swipe', () async {
final SerializableFinder pv = find.byValueKey('pageview');

await driver.waitFor(pv);
await driver.scroll(pv, -400, 0, Duration(milliseconds: 500));
await driver.getText(find.text('Second'));
await driver.scroll(pv, -400, 0, Duration(milliseconds: 500));
await driver.getText(find.text('Third'));

dx value of 400 is actually the width of the page. We can determine the size of every widget programmatically by using findRenderObject() . You can read more on this method and also on how to identify the height and width of each widget in this article.

Let’s run this test to see the result:

Now, we want to also test the flow by swiping back from the third page to first. The test for it as below:

await driver.scroll(pv, 400, 0, Duration(milliseconds: 500));
await driver.getText(find.text('Second'));
await driver.scroll(pv, 400, 0, Duration(milliseconds: 500));
await driver.getText(find.text('First'));

Here, since we need to swipe to left, we’ll pass positive dx value. Let’s run the complete test result to validate the entire flow.

Validating swipe based on scroll direction

The pages can also be swiped vertically by adding scrollDirection: Axis.vertical, in pageview widget. We can write test to validate this swipe direction by making use of dy argument. The complete test for this is as below:

test('test pageview vertical swipe', () async {
final SerializableFinder pv = find.byValueKey('pageview');

await driver.waitFor(pv);
await driver.scroll(pv, 0, -600, Duration(milliseconds: 500));
await driver.getText(find.text('Second'));
await driver.scroll(pv, 0, -600, Duration(milliseconds: 500));
await driver.getText(find.text('Third'));
await driver.scroll(pv, 0, 600, Duration(milliseconds: 500));
await driver.getText(find.text('Second'));
await driver.scroll(pv, 0, 600, Duration(milliseconds: 500));
await driver.getText(find.text('First'));

});

The value of dy here is passed as the height of the page, identified using findRenderObject() method as discussed and linked above.

And the result on Android and iOS is:

Android
iOS

driver.scroll() method also helps us to validate long press gesture on a widget (ex : button). For this, since we don’t need to perform any swipe, we’ll pass 0, 0 as the total offset for dx and dy and will tell driver to perform the action for 0.5 seconds. Let’s see what we get when we do that:

test('test button longpress', () async {
final SerializableFinder btn = find.byValueKey('button');

await driver.waitFor(btn);
await driver.scroll(btn, 0, 0, Duration(milliseconds: 500));
});

And the result is:

We can perform this long press validation on other widgets per requirement.

That’s all for now. Today we saw how to flutter driver method driver.scroll() helps us to test different type of gestures and how we can make use of it to write comprehensive end to end integration tests.

Thanks for reading and feel free to comment below your thoughts or any suggestions/feedback on this article. I am available on Twitter, LinkedIn, and Github.

My other articles on Flutter are:

--

--

Darshan Kawar
Flutter Community

Open Source Support Engineer For Flutter @nevercodeHQ. Android Nanodegree Certified. Previously, Android Automation Test Engineer.