install
source · Clone the upstream repo
git clone https://github.com/kevmoo/dash_skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/kevmoo/dash_skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.agent/skills/dart-matcher-best-practices" ~/.claude/skills/kevmoo-dash-skills-dart-matcher-best-practices && rm -rf "$T"
manifest:
.agent/skills/dart-matcher-best-practices/SKILL.mdsource content
Dart Matcher Best Practices
When to use this skill
Use this skill when:
- Writing assertions using
andexpect
.package:matcher - Migrating legacy manual checks to cleaner matchers.
- Debugging confusing test failures.
Core Matchers
1. Collections (hasLength
, contains
, isEmpty
, unorderedEquals
, containsPair
)
hasLengthcontainsisEmptyunorderedEqualscontainsPair-
:hasLength(n)- Prefer
overexpect(list, hasLength(n))
.expect(list.length, n) - Gives better error messages on failure (shows actual list content).
- Prefer
-
/isEmpty
:isNotEmpty- Prefer
overexpect(list, isEmpty)
.expect(list.isEmpty, true) - Prefer
overexpect(list, isNotEmpty)
.expect(list.isNotEmpty, true)
- Prefer
-
: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
orexpect(map[key], value)
.expect(map.containsKey(key), true)
2. Type Checks (isA<T>
and TypeMatcher<T>
)
isA<T>TypeMatcher<T>-
:isA<T>()- Prefer for inline assertions:
.expect(obj, isA<Type>()) - More concise and readable than
.TypeMatcher<Type>() - Allows chaining constraints using
..having()
- Prefer for inline assertions:
-
:TypeMatcher<T>- Prefer when defining top-level reusable matchers.
- Use
:constconst isMyType = TypeMatcher<MyType>(); - Chaining
works here too, but the resulting matcher is not.having()
.const
3. Object Properties (having
)
havingUse
.having() on isA<T>() or other TypeMatchers to check properties.
- Descriptive Names: Use meaningful parameter names in the closure (e.g.,
) instead of generic ones like(e) => e.message
to improve readability.p0
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
to ensure the future completes before the test continues.await expectLater(...)
.await expectLater(future, completion(equals(42)))
-
:throwsA(matcher)- Check that a future or function throws an exception.
.await expectLater(future, throwsA(isA<StateError>()))
(synchronous function throwing is fine withexpect(() => function(), throwsA(isA<ArgumentError>()))
).expect
5. Using expectLater
expectLaterUse
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
statements orif
loops for assertions; let matchers handle it.for - Specific Matchers: Use the most specific matcher available (e.g.,
for maps instead of checking keys manually).containsPair
Related Skills
- dart-test-fundamentals: Core concepts for structuring tests, lifecycles, and configuration.
- dart-checks-migration: Use this
skill if you are migrating tests from
to modernpackage:matcher
.package:checks