fake_async
fake_async copied to clipboard
Prevent accidental misuse of fake_async when using async/await
I use fakeAsync for some test cases in my daily work. I have accidentally written a test case which looks like this:
FakeAsync().run((fakeAsync) async {
functionWithTimer();
fakeAsync.elapse(t);
final x = await otherFunction();
expect(x, expectedX);
});
I only realized a few weeks later (after the code went through a review by multiple experienced engineers and it was submitted), that the expectation doesn't actually run. I could add a throw Exception()
after the expect
call, and my test case would still succeed. This is because asynchronous callbacks passed to FakeAsync.run are effectively not awaited.
Unfortunately I was not the only one making this mistake, but I have seen this in multiple code files.
I'm wondering whether this kind of errors could be prevented.
Here is a naive idea:
- We could disallow callbacks that return a Future in FakeAsync.run(). (Or allow it, but only with a special bool parameter for opt-in.)
- We could provide another similar function, that makes sure that the callback's result is awaited and fully runs. It could use a combination of awaits and flushTimers or flushMicrotasks calls to achieve this.
Please feel free to contact me if you need an example for the erroneous use-case or the possible solution.
An alternative solution could be https://github.com/dart-lang/fake_async/issues/43 I think
A duplicate report #51 has some handy repro cases. This test correctly fails:
test('test smoke test -- this test should fail', () {
fakeAsync((async) {
expect(true, isFalse);
});
});
But this one unexpectedly passes:
test('test smoke test -- this test should fail', () async {
fakeAsync((async) async {
expect(true, isFalse);
});
});
Then here's a slightly more complex example, which one might expect to pass:
import 'package:fake_async/fake_async.dart';
import 'package:test/test.dart';
void main() {
test('test smoke test -- this test should pass', () async {
await fakeAsync((async) async {
final future = doWork();
async.elapse(const Duration(seconds: 2));
final value = await future;
expect(value, 1);
});
});
}
Future<int> doWork() async {
await Future<void>.delayed(const Duration(seconds: 1));
return 1;
}
but instead it gets stuck and times out:
$ dart test main.dart
00:30 +0 -1: test smoke test -- this test should pass [E]
TimeoutException after 0:00:30.000000: Test timed out after 30 seconds. See https://pub.dev/packages/test#timeouts
dart:isolate _RawReceivePortImpl._handleMessage
To run this test again: /Users/bartek/fvm/versions/stable/bin/cache/dart-sdk/bin/dart test main.dart -p vm --plain-name 'test smoke test -- this test should pass'
00:30 +0 -1: Some tests failed.
Consider enabling the flag chain-stack-traces to receive more detailed exceptions.
For example, 'dart test --chain-stack-traces'.