In widget tests, how can I perform a "pull-down" gesture?
Asked Answered
S

2

5

I am using the RefreshIndicator widget - the usage looks something like this:

RefreshIndicator(
  onRefresh: () => refreshState(), // Invokes a method on a state object that is mocked in my test
  child: ...

Now I want to verify, that a "pull-down" gesture results in an invocation of refreshState:

testWidgets('Pulling down triggers refresh', (tester) async {
    var state = MockState(); // Class extending Mock from mocktail
    ...
    // pump widget, providing state as a ChangeNotifierProvider
    ...

    // SomeWidget is a child in the widget under test and effective child of RefreshIndicator, it's center is positioned towards the top of the screen
    var gesture = await tester.startGesture(tester.getCenter(find.byType(SomeWidget)));
    await gesture.moveBy(Offset(0, 750));
    await tester.pump();

    verify(() => state.refresh());
  });

Unfortunately, this results in a failing test, suggesting the method was not invoked:

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
  No matching calls (actually, no calls at all).
(If you called `verify(...).called(0);`, please instead use `verifyNever(...);`.)

EDIT:

As suggested by Michael, using tester.drag together with tester.pumpAndSettle instead of tester.pump resolved the issue

Snifter answered 13/7, 2021 at 5:49 Comment(0)
P
6

You can use the drag method from flutter_test.

API docs here: https://api.flutter.dev/flutter/flutter_test/WidgetController/drag.html

Example usage:

await tester.drag(find.byKey(ValueKey("Your Key")), Offset(0, 500));
Pedestal answered 13/7, 2021 at 6:46 Comment(3)
Thanks for answering! However, this does not work for me. Please refer to the edit in my original post, where I created a minimal example, where using tester.drag is not working eitherSnifter
The question was about how to perform a pull-down gesture, the code I posted should do that. However I think something else might be wrong with your test. Have you tried calling pumpAndSettle() instead of pump()? Pump only advances the 1frame, while pumpandsettle will keep advancing frames until there is nothing else to draw.Pedestal
It seems like using pump instead of pumpAndSettle was indeed part of the problem, thanks!Snifter
M
1

You can use tester.fling(finder,offset,speed) as in flutter tests.

Example:

testWidgets('RefreshIndicator', (final tester) async {
  var refreshCalled = false;
  await tester.pumpWidget(
    MaterialApp(
      home: RefreshIndicator(
        onRefresh: () async {
          refreshCalled = true;
        },
        child: ListView(
          children: ['A', 'B', 'C', 'D', 'E', 'F']
              .map((final item) => SizedBox(height: 200, child: Text(item)))
              .toList(),
        ),
      ),
    ),
  );

  await tester.fling(find.text('A'), const Offset(0, 400), 800);

  expect(
    tester.getSemantics(find.byType(RefreshProgressIndicator)),
    matchesSemantics(label: 'Refresh'),
  );

  await tester.pumpAndSettle();
  
  expect(refreshCalled, true);
});
Mercurochrome answered 21/3, 2023 at 12:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.