flutterdartriverpodwidget-test-flutterflutter-hooks

error `pumpAndSettle timed out` MAYBE due to riverpod


I'm stuck with a widget test and I could use some help
to reproduce the behavior please run the code sample below

import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'home_page.dart';

void main() => runApp(
      const ProviderScope(
        child: MaterialApp(
          home: Material(
            child: MyHomePage(),
          ),
        ),
      ),
    );
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

extension RoundX on double {
  double roundToPrecision(int n) {
    final f = pow(10, n);
    return (this * f).round() / f;
  }
}

final tasksPod = Provider<List<Future<void> Function()>>(
  (ref) => [
    for (var i = 0; i < 10; ++i)
      () async {
        await Future.delayed(kThemeAnimationDuration);
      }
  ],
);

final progressPod = Provider.autoDispose<ValueNotifier<double>>((ref) {
  final notifier = ValueNotifier<double>(0);
  ref.onDispose(notifier.dispose);
  return notifier;
});

class MyHomePage extends HookWidget {
  const MyHomePage() : super(key: const ValueKey('MyHomePage'));

  @override
  Widget build(BuildContext context) {
    final progress = useProvider(progressPod);
    final tasks = useProvider(tasksPod);
    useMemoized(() async {
      final steps = tasks.length;
      if (steps < 1) {
        progress.value = 1;
      } else {
        for (final task in tasks) {
          final current = progress.value;
          if (current >= 1) {
            break;
          }
          await task();
          final value = (current + 1 / steps).roundToPrecision(1);
          print('$value');
          progress.value = value;
        }
      }
    });
    return Center(
      child: ValueListenableBuilder<double>(
        valueListenable: progress,
        child: const FlutterLogo(),
        builder: (context, value, child) =>
            value < 1 ? const CircularProgressIndicator() : child!,
      ),
    );
  }
}

running the app everything is fine

āœ“  Built build/app/outputs/flutter-apk/app-debug.apk.
Installing build/app/outputs/flutter-apk/app.apk...                 4.7s
Syncing files to device Pixel 3a...                                 93ms

Flutter run key commands.
r Hot reload. šŸ”„šŸ”„šŸ”„
R Hot restart.
h Repeat this help message.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).

šŸ’Ŗ Running with sound null safety šŸ’Ŗ

An Observatory debugger and profiler on Pixel 3a is available at: http://127.0.0.1:36517/50vVndYZ3l4=/
I/flutter (19990): 0.1
I/flutter (19990): 0.2
I/flutter (19990): 0.3
I/flutter (19990): 0.4
I/flutter (19990): 0.5
I/flutter (19990): 0.6
I/flutter (19990): 0.7
The Flutter DevTools debugger and profiler on Pixel 3a is available at: http://127.0.0.1:9101?uri=http%3A%2F%2F127.0.0.1%3A36517%2F50vVndYZ3l4%3D%2F
I/flutter (19990): 0.8
I/flutter (19990): 0.9
I/flutter (19990): 1.0
Application finished.

but fails this test

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:timeout_issue/home_page.dart';

void main() {
  testWidgets(
      'WHEN tasks are not completed'
      'THEN shows `CircularProgressIndicator`', (tester) async {
    TestWidgetsFlutterBinding.ensureInitialized();

    await tester.runAsync(() async {
      await tester.pumpWidget(
        ProviderScope(
          child: const MaterialApp(
            home: Material(
              child: MyHomePage(),
            ),
          ),
        ),
      );

      await tester.pumpAndSettle(kThemeAnimationDuration);

      expect(
        find.byType(CircularProgressIndicator),
        findsOneWidget,
        reason: 'CircularProgressIndicator should be shown',
      );
    });
  });
}

with this output

00:05 +0: WHEN tasks are not completedTHEN shows `CircularProgressIndicator`                                                                                                                                
ā•ā•ā•” EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ā•žā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
The following assertion was thrown while running async test code:
pumpAndSettle timed out

When the exception was thrown, this was the stack:
#0      WidgetTester.pumpAndSettle.<anonymous closure> (package:flutter_test/src/widget_tester.dart:651:11)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
...
ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
00:05 +0 -1: WHEN tasks are not completedTHEN shows `CircularProgressIndicator` [E]                                                                                                                         
  Test failed. See exception logs above.
  The test description was: WHEN tasks are not completedTHEN shows `CircularProgressIndicator`
  
00:05 +0 -1: Some tests failed.                

the environment is

Flutter version 2.2.0-11.0.pre.176

environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  hooks_riverpod: ^0.14.0
  flutter_hooks: ^0.16.0

any help is apprecciated


Solution

  • I'd say the problem is related to using pumpAndSettle and an infinite animation (Circular progress indicator). You can try using pump without the settle to build frames yourself.

    https://api.flutter.dev/flutter/flutter_test/WidgetTester/pumpAndSettle.html