Marcin Szałek
Marcin Szałek

Reputation: 5069

Testing function containing async function in Dart

I want to test a function which invokes other async function and I don't know how to write it. Function would go like this:

function(X x, Y y) {
    x.doSomethingAsync().then((result) {
        if (result != null) {
            y.doSomething();
        }
    }
}

I would like to mock both X and Y, run X and than verify that y.doSomething() gets invoked. However I do not know how to wait for x.doSomethingAsync() to complete. I was thinking about doing some waiting before assertion but it doesn't seem like reliable solution.
Any help please? :)

Upvotes: 6

Views: 9390

Answers (2)

Mark Dappollone
Mark Dappollone

Reputation: 1530

The accepted answer doesn't actually answer the original question. And some of the comments are misleading.

The real answer to the original question is to use

await untilCalled()

To test a function which calls an async function and prescribes the result with then, inside the test you can do:

function(mockX, mockY);
await untilCalled(mockY.doSomething());
verify(mockY.doSomething).called(1)

This will cause the test to wait for the async operation to complete and call the doSomething() function, and then you can verify the results.

Be careful, though, because if the function you're waiting for is never called, the test will wait forever. It's wise when using untilCalled to also set a timeout:

await untilCalled(untilCalled(mockY.doSomething())).timeout(Duration(seconds: 1));

This way, if the function you're waiting for doesn't get called, the test will continue, and then fail.

Upvotes: 1

Rémi Rousselet
Rémi Rousselet

Reputation: 277037

You can use async/await in dart. Which would simplify quite a lot your function :

function(DoSomething x,  DoSomething y) async {
  final result = await x.doSomethingAsync();
  if (result != null) {
    y.doSomething();
  }
}

This way, the function will not complete until x.doSomething has completed. You can then test your function using the same async/await operators with an async test.

You'd have this :

test('test my function', () async {
  await function(x, y);
});

Okey, but how do I test if the functions got called ?

For this, you can use mockito which is a mock package for tests purposes.

Let's assume your x/y class is :

class DoSomething {
  Future<Object> doSomethingAsync() async {}
  void doSomething() {}
}

you could then use Mockito by mocking your class methods using :

// Mock class
class MockDoSomething extends Mock implements DoSomething {
}

finally you could use that mock inside your test by doing :

test('test my function', () async {
  final x = new MockDoSomething();
  final y = new MockDoSomething();
  // test return != null
  when(x.doSomethingAsync()).thenReturn(42);
  await function(x, y);

  verifyNever(x.doSomething());
  verify(x.doSomethingAsync()).called(1);
  // y.doSomething must not be called since x.doSomethingAsync returns 42
  verify(y.doSomething()).called(1);
  verifyNever(y.doSomethingAsync());

  // reset mock
  clearInteractions(x);
  clearInteractions(y);

  // test return == null
  when(x.doSomethingAsync()).thenReturn(null);
  await function(x, y);

  verifyNever(x.doSomething());
  verify(x.doSomethingAsync()).called(1);
  // y must not be called this x.doSomethingAsync returns null here
  verifyZeroInteractions(y);
});

Upvotes: 8

Related Questions