Skip to content
Development
Skill

/dart-matcher-best-practices

Best practices for using `expect` and `package:matcher`. Focuses on readable assertions, proper matcher selection, and avoiding common pitfalls.

BOOST
From plugin
dash-skills
14712 skills
Install
$ npx -y skills add kevmoo/dash_skills --skill dart-matcher-best-practices --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/dart-matcher-best-practices

Context preview

The summary Claude sees to decide when to auto-load this skill.

Best practices for using `expect` and `package:matcher`. Focuses on readable assertions, proper matcher selection, and avoiding common pitfalls.

SKILL.md

dart-matcher-best-practices.SKILL.md
name: dart-matcher-best-practices
description: |-
  Best practices for using `expect` and `package:matcher`.
  Focuses on readable assertions, proper matcher selection, and avoiding
  common pitfalls.
license: Apache-2.0
key_features:
  - Package matcher assertions
  - Readable test expectations
  - Matcher selection & refactoring

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.

When NOT to use (Abstention Guardrails)

Do NOT apply this skill or refactor assertions when:

  • **Non-Collection Properties**: The property happens to be named `.length` but

belongs to a non-collection class (e.g. geometric scalar magnitude on a `Vector`, duration on a buffer, or line segment distance). `hasLength(n)` expects an `int` on an `Iterable`, `Map`, or `String`.

  • **Custom Domain Booleans**: The boolean check tests an arbitrary domain

property (e.g. `expect(switch.isEnabled, isTrue)`). Do not invent non-existent matchers.

  • **Alternative Assertion Frameworks**: The package has adopted `package:checks`

(`check(x)...`). Do not revert `checks` back to `package:matcher`.

  • **Already Idiomatic Assertions**: Tests already use declarative matchers

(`hasLength`, `isEmpty`, `isNotEmpty`, `contains`, `containsPair`, `isA`, `throwsA`). Abstain and make zero modifications.

Discovery

To find candidates for improving matcher usage, search for suboptimal patterns:

Suboptimal Length Checks

Search for length checks that should use `hasLength`:

  • **Regex**: `expect\([^,]+.length,\s*`

Suboptimal Boolean Checks

Search for checks on boolean properties that have specific matchers:

  • **Regex**: `expect\([^,]+.isEmpty,\s*(true|equals\(true\))`
  • **Regex**: `expect\([^,]+.isNotEmpty,\s*(true|equals\(true\))`
  • **Regex**: `expect\([^,]+.contains\(.*\),\s*(true|equals\(true\))`

Suboptimal Map Lookups

Search for manual map lookups instead of `containsPair`:

  • **Regex**: `expect\([^,]+\[.*\],\s*`

Core Matchers

1. Collections (`hasLength`, `contains`, `isEmpty`, `unorderedEquals`, `containsPair`)

  • **`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.
  • Prefer over `expect(list.contains(item), true)`.
  • **`unorderedEquals(items)`**:
  • Verify contents regardless of order.
  • Prefer over `expect(list, containsAll(items))`.
  • **`containsPair(key, value)`**:
  • Verify a map contains a specific key-value pair.
  • Prefer over checking `expect(map[key], value)` or

`expect(map.containsKey(key), true)`.

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. Exception Testing (Avoid `try/catch` with `fail()`)

  • **Prefer `throwsA` over imperative `try/catch`**:
  • Legacy tests often use

`try { fn(); fail('should throw'); } catch (e) { expect(e, isA<T>()); }`.

  • Replace with declarative matchers:
    // Synchronous:
    expect(() => parseData('invalid'), throwsA(isA<FormatException>()));

    // Asynchronous:
    await expectLater(client.fetch('bad-url'), throwsA(isA<HttpException>()));
  • Common built-in exception matchers include `throwsArgumentError`,

`throwsStateError`, `throwsRangeError`, `throwsFormatException`, `throwsNoSuchMethodError`, and `throwsUnsupportedError`.

6. 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

1. **Readable Failures**: Choose matchers that produce clear error messages. 2. **Avoid Manual Logic**: Don't use `if` statements or `for` loops for assertions; let matchers handle it. 3. **Specific Matchers**: Use the most specific matcher available (e.g., `containsPair` for maps instead of checking keys manually).

Related Skills

  • **[d
Read more
Ships withdash-skills

Agent Skills for Dart and Flutter ecosytem

Get the whole plugin

Other skills on dash-skills.