How to catch an error coming from a Future in flutter widget test?
Wrap your code with await tester.runAsync(() async { .. }
From the official documentation runAsync<T>
Runs a callback that performs real asynchronous work.
This is intended for callers who need to call asynchronous methods where the methods spawn isolates or OS threads and thus cannot be executed synchronously by calling pump.
see below
testWidgets('This test should pass but fails', (tester) async { await tester.runAsync(() async { final future = Future<void>.error(42); await tester.pumpWidget(FutureBuilder( future: future, builder: (_, snapshot) { return Container(); }, )); }); });
EDIT:
(second issue OP raised)
in such cases use
Future.delayed(Duration.zero, () { tester.tap(find.text('GO'));});
Full snippet below
testWidgets('2nd try This test should pass but fails', (tester) async {Future future;await tester.runAsync(() async { await tester.pumpWidget( MaterialApp( home: Row( children: <Widget>[ FlatButton( child: const Text('GO'), onPressed: () { future = Future.error(42); }, ), FutureBuilder( future: future, builder: (_, snapshot) { return Container(); }, ), ], ), ), ); Future.delayed(Duration.zero, () {tester.tap(find.text('GO'));});});});
Edit 2:
It was later found that
Future.delayed(Duration.zero, () { tester.tap(find.text('GO')); });
is not being called.
Futures report errors to their listeners. If a Future
doesn't have listeners it informs the Zone
about the uncaught error (src). This is where the testing framework gets the error from.
One way to overcome this error is to wait for listeners before erroring the Future
.
testWidgets('This passes', (tester) async { final Completer completer = Completer(); await tester.pumpWidget(FutureBuilder( future: completer.future, builder: (_, snapshot) { return Container(); }, )); // has subscribers, doesn't inform Zone about uncought error completer.completeError(42); tester.pumpAndSettle(); });
I think that the problem is that you're not catching the error and that makes the app crash.
I've tried catching the error and the test passes:
here is the code:
testWidgets('This test should pass but fails', (tester) async { Future future; await tester.pumpWidget(MaterialApp( home: Row( children: <Widget>[ FlatButton( child: const Text('GO'), onPressed: () { future = Future.error(42).catchError((error) {}); }, ), FutureBuilder( future: future, builder: (_, snapshot) { return Container(); }, ), ], ), )); await tester.tap(find.text('GO')); });