1

I experience flakiness in my flutter driver tests.

The following code:

    await driver.tap(find.byValueKey('first-name-field'));
    await driver.enterText('');
    await Future.delayed(Duration(milliseconds: 17), () {});
    expect(
      await driver.getText(find.byValueKey('first-name-error')),
      equals('Field can't be empty.'),
    );
    await driver.enterText('$#@');
    await Future.delayed(Duration(milliseconds: 17), () {});
    expect(
      await driver.getText(find.byValueKey('first-name-error')),
      equals('Field contains unsupported characters.'),
    );
    await driver.enterText('Chris');
    await Future.delayed(Duration(milliseconds: 17), () {});
    await driver.waitForAbsent('first-name-error');

It has about 90% success rate, however 10% of the times I'm expecting "Field contains unsupported characters.", but I still have "Field can't be empty".

How the code works - TextEditingController listener is reporting change to ViewModel. ViewModel has a stream that provides error as string or null, View uses StreamBuilder to display error label or hide it.

To mitigate this issue I introduced a single frame delay (17 miliseconds) - it helped a bit, but still is flaky. I could extend the delay, but this comes with the cost of slower tests. I found a method that should make this test better:

await driver.waitFor(find.text('Field can't be empty.!'))

However, I don't want just to find any text on the screen, I want to inspect specifically first-name-error, as there is also last-name-error on the same screen.

Chris Rutkowski
  • 1,774
  • 1
  • 26
  • 36

1 Answers1

0

Flutter driver tests are frame synchronized by default. ie, it'll wait till there are no pending frames. Since you are telling driver to wait till 17 ms, it may not necessarily wait till that time for pending frame condition to be satisfied and thus, once driver is done waiting for 17 ms, it'll try to execute next statement which may not be active / visible to it. Hence, you may be seeing flakiness. To avoid this, you may want to wrap your code in runUnsynchronized() method which will help to continue the action with frame sync disabled. You can read more about this method here.

Hope this helps.

Darshan
  • 10,550
  • 5
  • 49
  • 61
  • runUnsynchronized() is used when there is an ongoing animation on the screen if I'm not mistaken. I updated my question after you replied. Waiting 17 ms is a workaround attempt, I put my problem in bold. – Chris Rutkowski Mar 20 '20 at 06:52