Given this minimal test
test('ignore failures', () {
Future<void> failingFunc() async {
throw Exception('BOOOOM');
}
failingFunc();
});
The test fails with this output :
Exception: BOOOOM
main.<fn>.<fn>.<fn>.failingFunc
test/…/auth/auth_action_bloc_test.dart:374
main.<fn>.<fn>.<fn>
test/…/auth/auth_action_bloc_test.dart:377
===== asynchronous gap ===========================
dart:async _AsyncAwaitCompleter.completeError
test/unit/features/auth/auth_action_bloc_test.dart main.<fn>.<fn>.<fn>.failingFunc
test/…/auth/auth_action_bloc_test.dart:1
main.<fn>.<fn>.<fn>
test/…/auth/auth_action_bloc_test.dart:377
In don't understand why that test fails.
My understanding is that failingFunc()
would fail “silently” because it’s not awaited. (i.e. the exception would be thrown “somewhere else”, and just ignored)
I’m (almost) certain that such a thing in production code would work just fine.
The following test does fail the way I expect it to (i.e. adding an await
) :
test('ignore failures', () async {
Future<void> failingFunc() async {
throw Exception('BOOOOM');
}
await failingFunc();
});
and it fails as expected with
Exception: BOOOOM
main.<fn>.<fn>.<fn>.failingFunc
test/…/auth/auth_action_bloc_test.dart:374
main.<fn>.<fn>.<fn>
test/…/auth/auth_action_bloc_test.dart:377
CodePudding user response:
The exception will be thrown somewhere else, but it will most certainly not be ignored.
An uncaught asynchronous error, which this is, is reported to the current Zone
's uncaught error handler. If you do nothing special, that's the root zone, which will terminate the program on an uncaught error.
Well, it will do so in native code. On the web, you can't crash the browser, so it will just be treated as an unhandled JavaScript error, which may or may not be ignored. It's still bad style, and makes debugging harder, so you shouldn't let errors be unhandled in production.
The test
package runs each test in a new zone with an uncaught-error handler, so it knows which test caused the exception. It also recognizes that the error happened after it had already considered that test done and successful. If you're lucky, it will go back and mark the test as failing from throwing that error, even if it does so much later. If unlucky, and slow, and all tests are done and the test runner has printed the results, then it might be too late to go back and change the result.
Example:
import "package:test/test.dart";
void main() {
test("example", () {
Future.delayed(Duration(seconds: 1), () => throw "error");
});
test("delayed", () => Future.delayed(Duration(seconds: 2)));
}
In this example, the test
package recognizes that the "example"
test fails, even if it does so after it has returned and pretended all is well.
If you change the duration of "delayed"
to zero, then it reports that both tests are successful, before the first tests uncaught error happens, and it just prints 00:00 2: All tests passed!
.
If you actually want to ignore errors from a future, you have to do something. Since Dart 2.14, you can write:
failingFunc().ignore();
The ignore()
is an extension method on Future
declared in dart:async
/dart:core
which catches and ignores errors from the future
it's called on.
Use with care, errors might be significant.