Assertions in Dart and Flutter tests: collection matchers

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

  • collection matchers,
  • string matchers,
  • iterable matchers,
  • map matchers.

In this series:

Collection matchers

By β€œcollection” I mean String, Iterable, and Map.

Size matchers

The pair of isEmpty and isNotEmpty matchers call respective .isEmpty or .isNotEmpty getters on a result, and expect them to return true:

test('expect: isEmpty βœ…', () {
final result = [];
expect(result, isEmpty);
});
test('expect: isEmpty ❌', () {
final result = '0';
expect(result, isEmpty);
});
test('expect: isNotEmpty βœ…', () {
final result = {0: '0'};
expect(result, isNotEmpty);
});

When used with the type that does not have isEmpty or isNotEmpty methods:

test('expect: isEmpty ❌', () {
final result = 0;
expect(result, isNotEmpty);
});

isEmpty and isNotEmpty matchers fail the test with the following output:

Expected: non-empty
Actual: <0>
NoSuchMethodError: Class 'int' has no instance getter 'isNotEmpty'.
Receiver: 0
Tried calling: isNotEmpty

The hasLength matcher follows the same principle and calls .length getter on the passed value:

test('expect: hasLength βœ…', () {
final result = '0';
expect(result, hasLength(1));
});

When the value does not have a .length getter:

test('expect: hasLength ❌', () {
final result = 0;
expect(result, hasLength(1));
});

the test fails:

Expected: an object with length of <1>
Actual: <0>
Which: has no length property

Content matchers

The contains matcher has different logic depending on the value it is applied to.

For a String it means substring matching:

test('expect: contains βœ…', () {
final result = 'result';
expect(result, contains('res'));
});

For a Map it means the map has the key:

test('expect: contains βœ…', () {
final result = {0: Result(0)};
expect(result, contains(0));
});

And for Iterable it means there is an element matching the matcher that is passed inside contains matcher. In this test first a predicate matcher is used, and then an implicit equals:

test('expect: contains βœ…', () {
final result = [Result(0), Result(1)];
expect(result, contains(predicate<Result>((r) => r.value == 0)));
expect(result, contains(Result(1)));
});

The isIn matcher is the opposite to the contains matcher:

test('expect: isIn βœ…', () {
final result = 'res';
expect(result, isIn('result'));
});
test('expect: isIn βœ…', () {
final result = Result(0);
expect(result, isIn([Result(0), Result(1)]));
});

String matchers

In addition to the collection matchers above, that work for String, there is a couple of matchers more.

Content matchers

startsWith, endsWith matchers check String content along the edges:

test('expect: startsWith βœ…', () {
final result = 'result';
expect(result, startsWith('res'));
});
test('expect: endsWith βœ…', () {
final result = 'result';
expect(result, endsWith('ult'));
});

matches

The matches matcher can either accept another String:

test('expect: matches βœ…', () {
final result = 'result';
expect(result, matches('esul'));
});

or a RegExp:

test('expect: matches βœ…', () {
final result = 'result';
expect(result, matches(RegExp('r[a-z]{4}t')));
});

Iterable matchers

In addition to the collection matchers above that work for List and Set, there are a few more matchers.

every & any

Matchers everyElement and anyElement verify that all or some elements satisfy a matcher or are equal to a value they accepted as a parameter:

test('expect: everyElement βœ…', () {
final result = [Result(0), Result(1)];
expect(result, everyElement(isResult));
});
test('expect: anyElement βœ…', () {
final result = {0, 1};
expect(result, anyElement(0));
});

Content matchers

Matchers containsAll and containsAllInOrder verify that the Iterable passed as a parameter is a subset of the actual Iterable, optionally verifying items’ order:

test('expect: containsAll βœ…', () {
final result = [Result(0), Result(1), Result(2)];
expect(result, containsAll([Result(1), Result(0)]));
});
test('expect: containsAllInOrder βœ…', () {
final result = {0, 1, 2};
expect(result, containsAllInOrder({0, 1}));
});

The actual Iterable can have additional elements.

Matchers orderedEquals and unorderedEquals check that the actual Iterable is of the same length and contains the same elements as the passed Iterable, optionally verifying items’ order:

test('expect: orderedEquals βœ…', () {
final result = [Result(0), Result(1)];
expect(result, orderedEquals([Result(0), Result(1)]));
});
test('expect: unorderedEquals βœ…', () {
final result = {0, 1};
expect(result, unorderedEquals({1, 0}));
});

Map matchers

In addition to collection matchers that work for Map, there are just a couple more.

containsValue

The containsValue matcher checks if the actual’s .containsValue() method returns true:

test('expect: containsValue βœ…', () {
final result = {0: Result(0)};
expect(result, containsValue(Result(0)));
});

containsPair

The containsPair matcher checks both pair’s key and value, where value can be another matcher:

test('expect: containsPair βœ…', () {
final result = {0: Result(0)};
expect(result, containsPair(0, isResult));
expect(result, containsPair(0, Result(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