dart-matcher-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDart Matcher Best Practices
Dart Matcher 最佳实践
When to use this skill
什么时候使用本技能
Use this skill when:
- Writing assertions using and
expect.package:matcher - Migrating legacy manual checks to cleaner matchers.
- Debugging confusing test failures.
当你遇到以下场景时可使用本技能:
- 使用和
expect编写断言时package:matcher - 将遗留的手动校验逻辑迁移为更简洁的匹配器实现时
- 排查令人困惑的测试失败问题时
Core Matchers
核心匹配器
1. Collections (hasLength
, contains
, isEmpty
, unorderedEquals
, containsPair
)
hasLengthcontainsisEmptyunorderedEqualscontainsPair1. 集合类匹配器(hasLength
、contains
、isEmpty
、unorderedEquals
、containsPair
)
hasLengthcontainsisEmptyunorderedEqualscontainsPair-
:
hasLength(n)- Prefer over
expect(list, hasLength(n)).expect(list.length, n) - Gives better error messages on failure (shows actual list content).
- Prefer
-
/
isEmpty:isNotEmpty- Prefer over
expect(list, isEmpty).expect(list.isEmpty, true) - Prefer over
expect(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 or
expect(map[key], value).expect(map.containsKey(key), true)
-
:
hasLength(n)- 优先使用而非
expect(list, hasLength(n))expect(list.length, n) - 失败时会提供更友好的错误信息(会展示列表的实际内容)
- 优先使用
-
/
isEmpty:isNotEmpty- 优先使用而非
expect(list, isEmpty)expect(list.isEmpty, true) - 优先使用而非
expect(list, isNotEmpty)expect(list.isNotEmpty, true)
- 优先使用
-
:
contains(item)- 无需手动遍历即可校验元素是否存在
- 优先使用它代替
expect(list.contains(item), true)
-
:
unorderedEquals(items)- 不考虑顺序校验集合内容
- 优先使用它代替
expect(list, containsAll(items))
-
:
containsPair(key, value)- 校验Map中是否包含指定的键值对
- 优先使用它代替或者
expect(map[key], value)的写法expect(map.containsKey(key), true)
2. Type Checks (isA<T>
and TypeMatcher<T>
)
isA<T>TypeMatcher<T>2. 类型校验(isA<T>
和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
-
:
isA<T>()- 优先用于行内断言:
expect(obj, isA<Type>()) - 比更简洁易读
TypeMatcher<Type>() - 支持通过链式添加约束
.having()
- 优先用于行内断言:
-
:
TypeMatcher<T>- 优先用于定义顶层可复用的匹配器
- 使用修饰:
constconst isMyType = TypeMatcher<MyType>(); - 同样支持链式调用,但生成的匹配器不支持
.having()修饰const
3. Object Properties (having
)
having3. 对象属性校验(having
)
havingUse on or other TypeMatchers to check properties.
.having()isA<T>()- Descriptive Names: Use meaningful parameter names in the closure (e.g.,
) instead of generic ones like
(e) => e.messageto improve readability.p0
dart
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.
在或其他TypeMatcher上使用来校验属性。
isA<T>().having()- 描述性命名:在闭包中使用有意义的参数名(例如
)而非
(e) => e.message这类泛用名称,提升可读性。p0
dart
expect(person, isA<Person>()
.having((p) => p.name, 'name', 'Alice')
.having((p) => p.age, 'age', greaterThan(18)));这种写法会生成详细的失败信息,明确指出具体是哪个属性校验不通过。
4. Async Assertions
4. 异步断言
-
:
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 with
expect(() => function(), throwsA(isA<ArgumentError>()))).expect
-
:
completion(matcher)- 等待Future完成并校验其返回值
- 优先使用,确保测试继续执行前Future已完成
await expectLater(...) - 示例:
await expectLater(future, completion(equals(42)))
-
:
throwsA(matcher)- 校验Future或函数是否会抛出异常
- 示例:
await expectLater(future, throwsA(isA<StateError>())) - 同步函数抛出异常的场景直接用即可:
expectexpect(() => function(), throwsA(isA<ArgumentError>()))
5. Using expectLater
expectLater5. expectLater
的使用
expectLaterUse when testing async behavior to ensure proper
sequencing.
await expectLater(...)dart
// 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!测试异步行为时使用,确保执行顺序正确。
await expectLater(...)dart
// 推荐写法:校验副作用前先等待Future执行完成
await expectLater(future, completion(equals(42)));
expect(sideEffectState, equals('done'));
// 不推荐写法:副作用校验可能在Future完成前就执行
expect(future, completion(equals(42)));
expect(sideEffectState, equals('done')); // 竞态条件!Principles
原则
- Readable Failures: Choose matchers that produce clear error messages.
- Avoid Manual Logic: Don't use statements or
ifloops 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
- 失败信息可读性:选择能生成清晰错误信息的匹配器
- 避免手动逻辑:不要在断言中使用语句或
if循环,交给匹配器处理即可for - 匹配器专一性:使用最贴合场景的特定匹配器(例如针对Map使用,而非手动校验键)
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 modern
package:matcher.package:checks
- dart-test-fundamentals:测试结构、生命周期和配置的核心概念
- dart-checks-migration:如果你要将测试从迁移到新版
package:matcher,可以使用该技能package:checks