Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent accidental misuse of fake_async when using async/await #38

Open
tdanyluk opened this issue Aug 2, 2021 · 2 comments
Open

Prevent accidental misuse of fake_async when using async/await #38

tdanyluk opened this issue Aug 2, 2021 · 2 comments

Comments

@tdanyluk
Copy link

tdanyluk commented Aug 2, 2021

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.

@rrousselGit
Copy link

An alternative solution could be #43 I think

@gnprice
Copy link

gnprice commented May 17, 2023

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'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants