Assertions in Dart and Flutter tests: mock invocations and parameters

This is the part of the ultimate cheat sheet dedicated to:

  • testing invocations with verify(),
  • testing invocation parameters with any().

In this series:

So far, we have used expect() function to compare the results of some operations with the expected values. There is another category of checks developers constantly perform: ensuring that some side effect was triggered.

Verify invocations

For that, the verify() function is used:

test('verify ❌', () {
final mock = MockService();
verify(() => mock.sideEffect(0));
});

This verify() function comes from the mocktail package. The mockito package provides similar functionality.

For context, here is the definition of MockService:

abstract class Service { 
void sideEffect(int value, {Result? result});
}

class MockService extends Mock implements Service {}

The verify() function ensures that provided method was called at least once. The test above fails because the sideEffect() method was never called, with the following output:

No matching calls (actually, no calls at all).

To ensure certain method was called exactly N times, use the called() method of verify() function result:

test('verify ❌', () {
final mock = MockService();
verify(() => mock.sideEffect(0)).called(1);
});

To ensure certain method was never called, use the verifyNever() function:

test('verifyNever βœ…', () {
final mock = MockService();
verifyNever(() => mock.sideEffect(0));
});

Verify parameters

In the example above, the sideEffect() method accepts two parameters. In case their values do not matter, use the any() object that can represent literally any value:

test('verify: any βœ…', () {
final mock = MockService()..sideEffect(0, result: Result(0));
verify(() => mock.sideEffect(any(), result: any(named: 'result')));
});

In case invocation parameters matter, real values can be used instead of any():

test('verify: any βœ…', () {
final mock = MockService()..sideEffect(0, result: Result(0));
verify(() => mock.sideEffect(0, result: Result(0)));
});

Similar to equals matcher, verify will compare parameters using the equality operator. Thus, for the above test to pass, the Result class has to have the operator == overridden, as was shown before. Otherwise, the test fails with the following output:

No matching calls. 
All calls: MockService.sideEffect(0, {result: Result{value: 0}})

In case you are only interested in checking those parameters met some condition, any() call can accept one of the matchers we looked so closely at before as that parameter:

test('verify: any that βœ…', () {
final mock = MockService()..sideEffect(0, result: Result(0));
verify(() => mock.sideEffect(
any(that: isZero),
result: any(named: 'result', that: allOf(isResult, hasValue(0))),
));
});

Originally published at Invertase blog. Check out their awesome Authors Program!

Hi! πŸ‘‹πŸ» I’m Anna, Google Developer Expert in Flutter from Ukraine πŸ‡ΊπŸ‡¦ Follow me on Twitter, GitHub, YouTube, Medium to get notifications about my latest work.

It’s early 2023, and we in Ukraine are still fighting against russians committing genocide on our lands. If you find this content useful and have a coin to spare, support us with your donations. Stand with Ukraine!

--

--

Anna Leushchenko πŸ‘©β€πŸ’»πŸ’™πŸ“±πŸ‡ΊπŸ‡¦

Google Developer Expert in Dart and Flutter | Author, speaker at tech events, mentor, OSS contributor | Passionate mobile apps creator