Dart Matcher Best Practices
When to use this skill
Use this skill when:
- Writing assertions using
expect and package:matcher.
- Migrating legacy manual checks to cleaner matchers.
- Debugging confusing test failures.
Core Matchers
1. Collections (hasLength, contains, isEmpty)
hasLength(n):
- Prefer
expect(list, hasLength(n)) over expect(list.length, n).
- Gives better error messages on failure (shows actual list content).
isEmpty / isNotEmpty:
- Prefer
expect(list, isEmpty) over expect(list.isEmpty, true).
- Prefer
expect(list, isNotEmpty) over expect(list.isNotEmpty, true).
contains(item):
- Verify existence without manual iteration.
unorderedEquals(items):
- Verify contents regardless of order.
2. Type Checks (isA<T> and TypeMatcher<T>)
isA<T>():
- Prefer for inline assertions:
expect(obj, isA<Type>()).
- More concise and readable than
TypeMatcher<Type>().
- Allows chaining constraints using
.having().
TypeMatcher<T>:
- Prefer when defining top-level reusable matchers.
- Use
const: const isMyType = TypeMatcher<MyType>();
- Chaining
.having() works here too, but the resulting matcher is not const.
3. Object Properties (having)
Use .having() on isA<T>() or other TypeMatchers to check properties.
- Descriptive Names: Use meaningful parameter names in the closure (e.g.,
(e) => e.message) instead of generic ones like p0 to improve readability.
expect(person, isA<Person>()
.having((p) => p.name, 'name', 'Alice')
.having((p) => p.age, 'age', greaterThan(18)));
This provides detailed failure messages indicating exactly which property
failed.
4. Async Assertions
completion(matcher):
- Wait for a future to complete and check its value.
- Prefer
await expectLater(...) to ensure the future completes before
the test continues.
await expectLater(future, completion(equals(42))).
throwsA(matcher):
- Check that a future or function throws an exception.
await expectLater(future, throwsA(isA<StateError>())).
expect(() => function(), throwsA(isA<ArgumentError>())) (synchronous
function throwing is fine with expect).
5. Using expectLater
Use await expectLater(...) when testing async behavior to ensure proper
sequencing.
// GOOD: Waits for future to complete before checking side effects
await expectLater(future, completion(equals(42)));
expect(sideEffectState, equals('done'));
// BAD: Side effect check might run before future completes
expect(future, completion(equals(42)));
expect(sideEffectState, equals('done')); // Race condition!
Principles
- Readable Failures: Choose matchers that produce clear error messages.
- Avoid Manual Logic: Don't use
if statements or for loops for
assertions; let matchers handle it.
- Specific Matchers: Use the most specific matcher available (e.g.,
containsPair for maps instead of checking keys manually).
Related Skills
1---2name: dart-matcher-best-practices3description: Best practices for using `expect` and `package:matcher`. Focuses on readable assertions, proper matcher selection, and avoiding common pitfalls.4license: Apache-2.05---67# Dart Matcher Best Practices89## When to use this skill10Use this skill when:11- Writing assertions using `expect` and `package:matcher`.12- Migrating legacy manual checks to cleaner matchers.13- Debugging confusing test failures.1415## Core Matchers1617### 1. Collections (`hasLength`, `contains`, `isEmpty`)1819- **`hasLength(n)`**:20 - Prefer `expect(list, hasLength(n))` over `expect(list.length, n)`.21 - Gives better error messages on failure (shows actual list content).2223- **`isEmpty` / `isNotEmpty`**:24 - Prefer `expect(list, isEmpty)` over `expect(list.isEmpty, true)`.25 - Prefer `expect(list, isNotEmpty)` over `expect(list.isNotEmpty, true)`.2627- **`contains(item)`**:28 - Verify existence without manual iteration.2930- **`unorderedEquals(items)`**:31 - Verify contents regardless of order.3233### 2. Type Checks (`isA<T>` and `TypeMatcher<T>`)3435- **`isA<T>()`**:36 - Prefer for inline assertions: `expect(obj, isA<Type>())`.37 - More concise and readable than `TypeMatcher<Type>()`.38 - Allows chaining constraints using `.having()`.3940- **`TypeMatcher<T>`**:41 - Prefer when defining top-level reusable matchers.42 - **Use `const`**: `const isMyType = TypeMatcher<MyType>();`43 - Chaining `.having()` works here too, but the resulting matcher is not `const`.4445### 3. Object Properties (`having`)4647Use `.having()` on `isA<T>()` or other TypeMatchers to check properties.4849- **Descriptive Names**: Use meaningful parameter names in the closure (e.g.,50 `(e) => e.message`) instead of generic ones like `p0` to improve readability.5152```dart53expect(person, isA<Person>()54 .having((p) => p.name, 'name', 'Alice')55 .having((p) => p.age, 'age', greaterThan(18)));56```5758This provides detailed failure messages indicating exactly which property59failed.6061### 4. Async Assertions6263- **`completion(matcher)`**:64 - Wait for a future to complete and check its value.65 - **Prefer `await expectLater(...)`** to ensure the future completes before66 the test continues.67 - `await expectLater(future, completion(equals(42)))`.6869- **`throwsA(matcher)`**:70 - Check that a future or function throws an exception.71 - `await expectLater(future, throwsA(isA<StateError>()))`.72 - `expect(() => function(), throwsA(isA<ArgumentError>()))` (synchronous73 function throwing is fine with `expect`).7475### 5. Using `expectLater`7677Use `await expectLater(...)` when testing async behavior to ensure proper78sequencing.7980```dart81// GOOD: Waits for future to complete before checking side effects82await expectLater(future, completion(equals(42)));83expect(sideEffectState, equals('done'));8485// BAD: Side effect check might run before future completes86expect(future, completion(equals(42)));87expect(sideEffectState, equals('done')); // Race condition!88```8990## Principles91921. **Readable Failures**: Choose matchers that produce clear error messages.932. **Avoid Manual Logic**: Don't use `if` statements or `for` loops for94 assertions; let matchers handle it.953. **Specific Matchers**: Use the most specific matcher available (e.g.,96 `containsPair` for maps instead of checking keys manually).9798## Related Skills99100- **[`dart-test-fundamentals`](../dart-test-fundamentals/SKILL.md)**: Core101 concepts for structuring tests, lifecycles, and configuration.102- **[`dart-checks-migration`](../dart-checks-migration/SKILL.md)**: Use this103 skill if you are migrating tests from `package:matcher` to modern104 `package:checks`.