skill: add dart skills
This commit is contained in:
528
.agents/skills/dart-migrate-to-checks-package/SKILL.md
Normal file
528
.agents/skills/dart-migrate-to-checks-package/SKILL.md
Normal file
@@ -0,0 +1,528 @@
|
||||
---
|
||||
name: dart-migrate-to-checks-package
|
||||
description: |-
|
||||
Replace the usage of `expect` and similar functions from `package:matcher`
|
||||
to `package:checks` equivalents.
|
||||
metadata:
|
||||
model: models/gemini-3.1-pro-preview
|
||||
last_modified: Tue, 09 Jun 2026 19:30:00 GMT
|
||||
---
|
||||
# Migrating Dart Tests to Package Checks
|
||||
|
||||
Use this skill when you need to migrate a Dart test suite from the legacy
|
||||
`package:matcher` (which is exported by default from `package:test/test.dart`)
|
||||
to the modern, type-safe, and literate `package:checks` assertion library.
|
||||
|
||||
## Contents
|
||||
- [When to Use This Skill](#when-to-use-this-skill)
|
||||
- [How to Use This Skill (The Workflow)](#how-to-use-this-skill-the-workflow)
|
||||
- [Key Syntax Differences and Pitfalls](#key-syntax-differences-and-pitfalls)
|
||||
- [Matcher-to-Checks Mapping Table](#matcher-to-checks-mapping-table)
|
||||
- [Matchers with No Direct Replacements](#matchers-with-no-direct-replacements)
|
||||
- [Strategies for Discovery](#strategies-for-discovery)
|
||||
- [Examples](#examples)
|
||||
|
||||
---
|
||||
|
||||
## When to Use This Skill
|
||||
- When asked to "migrate tests to checks", "use package:checks", or
|
||||
"modernize test assertions".
|
||||
- When updating legacy test suites where static type safety, better
|
||||
autocomplete in IDEs, and highly detailed failure diagnostics are desired.
|
||||
|
||||
---
|
||||
|
||||
## How to Use This Skill (The Workflow)
|
||||
|
||||
Follow this structured workflow to safely and systematically migrate a test suite:
|
||||
|
||||
### 1. Dependency Setup
|
||||
- Add `package:checks` as a `dev_dependency` in `pubspec.yaml`:
|
||||
```bash
|
||||
dart pub add dev:checks
|
||||
```
|
||||
- Remove `package:matcher` if it is explicitly listed under `dev_dependencies`
|
||||
(it is typically transitively included by `package:test`, which is fine).
|
||||
|
||||
### 2. Identify and Plan Target Files
|
||||
- Use the grep patterns in [Strategies for Discovery](#strategies-for-discovery)
|
||||
to locate all test files containing legacy `expect` or `expectLater` calls.
|
||||
- Decide whether to migrate files fully or incrementally.
|
||||
|
||||
### 3. Migrating a File (Incremental or Full)
|
||||
For any target test file:
|
||||
1. **Update Imports**:
|
||||
- Replace the generic `import 'package:test/test.dart';` with:
|
||||
```dart
|
||||
import 'package:test/scaffolding.dart';
|
||||
import 'package:checks/checks.dart';
|
||||
```
|
||||
- **For Incremental Migration**: If you only want to migrate some test cases
|
||||
in the file, or want to migrate one step at a time, add:
|
||||
```dart
|
||||
import 'package:test/expect.dart'; // Temporarily allows legacy expect()
|
||||
```
|
||||
2. **Translate Assertions**: Rewrite legacy `expect` and `expectLater` calls
|
||||
to `check` syntax following the [Key Syntax Differences and
|
||||
Pitfalls](#key-syntax-differences-and-pitfalls) and the
|
||||
[Matcher-to-Checks Mapping Table](#matcher-to-checks-mapping-table).
|
||||
3. **Verify via Compiler**: If migrating fully, remove the `import
|
||||
'package:test/expect.dart';` line. Any remaining un-migrated `expect`
|
||||
calls will immediately surface as compiler errors, making them easy to
|
||||
find and fix.
|
||||
|
||||
### 4. Verification and Feedback Loops
|
||||
- **Static Analysis**: Run static analysis on the target package:
|
||||
```bash
|
||||
dart analyze
|
||||
```
|
||||
Pay close attention to generic type parameters on `.isA<Type>()` and
|
||||
ensure asynchronous expectations are properly awaited (check for
|
||||
`unawaited_futures` warnings).
|
||||
- **Run Tests**: Execute the tests to verify both behavior and correct
|
||||
assertion runtime logic:
|
||||
```bash
|
||||
dart test
|
||||
```
|
||||
If a test fails, review the extremely detailed failure output of
|
||||
`package:checks` to diagnose if the test is genuinely failing or if the
|
||||
expectation was translated incorrectly.
|
||||
|
||||
---
|
||||
|
||||
## Key Syntax Differences and Pitfalls
|
||||
|
||||
> [!IMPORTANT]
|
||||
> A line-for-line translation can sometimes introduce subtle bugs or false
|
||||
> passes. Always review these key differences carefully:
|
||||
|
||||
### 1. Collection Equality Pitfall (`equals` vs `deepEquals`)
|
||||
- **Legacy Matcher**: `expect(actual, expected)` or `expect(actual,
|
||||
equals(expected))` performed a **deep equality check** if the arguments
|
||||
were collections (Lists, Maps, Sets).
|
||||
- **Package Checks**: `.equals(expected)` corresponds strictly to
|
||||
`operator ==`. Since Dart collections do not override `operator ==` for
|
||||
element-wise comparison, using `.equals` on a collection will check for
|
||||
*identity* and almost certainly fail at runtime.
|
||||
- **Remediation**: You **must** replace collection equality assertions with
|
||||
`.deepEquals(expected)`.
|
||||
```dart
|
||||
// BEFORE (Matcher)
|
||||
expect(myList, [1, 2, 3]);
|
||||
|
||||
// AFTER (Checks)
|
||||
check(myList).deepEquals([1, 2, 3]);
|
||||
```
|
||||
|
||||
### 2. The `reason` Parameter is now `because`
|
||||
- **Legacy Matcher**: The explanation was passed as a trailing named
|
||||
argument `reason` to `expect`:
|
||||
```dart
|
||||
expect(actual, expectation, reason: 'Explanation');
|
||||
```
|
||||
- **Package Checks**: The explanation is passed as the named argument
|
||||
`because` to the `check` function *before* the actual subject:
|
||||
```dart
|
||||
check(because: 'Explanation', actual).expectation();
|
||||
```
|
||||
|
||||
### 3. Regular Expression Matching (`matches` vs `matchesPattern`)
|
||||
- **Legacy Matcher**: The `matches(pattern)` matcher automatically converted
|
||||
a `String` argument into a `RegExp` (e.g., `matches(r'\d')` matched `'1'`).
|
||||
- **Package Checks**: `.matchesPattern(pattern)` treats a `String` argument
|
||||
as a literal string pattern.
|
||||
- **Remediation**: To match using a regular expression, you must explicitly
|
||||
pass a `RegExp` object:
|
||||
```dart
|
||||
// BEFORE (Matcher)
|
||||
expect(someString, matches(r'\d+'));
|
||||
|
||||
// AFTER (Checks)
|
||||
check(someString).matchesPattern(RegExp(r'\d+'));
|
||||
```
|
||||
|
||||
### 4. Property Extraction (`TypeMatcher.having` vs `.has`)
|
||||
- **Legacy Matcher**: Chained field/property expectations used
|
||||
`TypeMatcher.having(feature, description, matcher)`:
|
||||
```dart
|
||||
expect(actual, isA<Person>().having((p) => p.name, 'name', startsWith('A')));
|
||||
```
|
||||
- **Package Checks**: The `.has(feature, description)` extension is
|
||||
available on all `Subject`s, takes one fewer argument, and returns a new
|
||||
`Subject` representing that property. You chain expectations directly off
|
||||
it:
|
||||
```dart
|
||||
check(actual).isA<Person>().has((p) => p.name, 'name').startsWith('A');
|
||||
```
|
||||
|
||||
### 5. Synchronous vs. Asynchronous `throws`
|
||||
- **Legacy Matcher**: In `package:matcher`, `throwsA` behaved similarly for both
|
||||
synchronous closures and asynchronous futures when wrapped in `expect` or
|
||||
`expectLater`.
|
||||
- **Package Checks**: The `.throws<E>()` expectation behaves differently and
|
||||
has different return types depending on whether the subject is synchronous or
|
||||
asynchronous:
|
||||
- **Synchronous** (`Subject<T Function()>`): `.throws<E>()` returns a
|
||||
`Subject<E>` synchronously. This **does not** accept a callback argument!
|
||||
You chain or cascade expectations directly off the returned `Subject<E>`:
|
||||
```dart
|
||||
// YES (Synchronous chaining)
|
||||
check(() => triggerSyncError()).throws<ArgumentError>()
|
||||
..has((e) => e.message, 'message').equals('invalid input');
|
||||
|
||||
// NO (Passing a callback to sync throws will cause a compiler error!)
|
||||
check(() => triggerSync").throws<ArgumentError>((it) => ...); // ERROR!
|
||||
```
|
||||
- **Asynchronous** (`Subject<Future<T>>`): `.throws<E>()` returns
|
||||
`Future<void>`. Because you cannot chain directly off a `Future<void>`, this
|
||||
**requires** an inspection callback:
|
||||
```dart
|
||||
// YES (Asynchronous callback)
|
||||
await check(triggerAsyncError()).throws<ArgumentError>((it) => it
|
||||
..has((e) => e.message, 'message').equals('invalid input'));
|
||||
```
|
||||
- **Crucial Pitfall**: Trying to chain expectations directly after an awaited
|
||||
asynchronous `.throws<E>()` (e.g.,
|
||||
`await check(future).throws<E>().equals(...)`) will fail to compile
|
||||
because it returns `Future<void>`.
|
||||
|
||||
### 6. RegExp / Pattern Equality
|
||||
- **Legacy Matcher**: In `package:matcher`, `expect(myPattern,`
|
||||
`equals(RegExp('Hello')))` worked because the matcher comparison rules
|
||||
handled RegExp instances.
|
||||
- **Package Checks**: `.equals()` uses strict Dart `==` equality. Since separate
|
||||
`RegExp` instances do not satisfy `==`, using `.equals()` will fail at runtime.
|
||||
- **Remediation**: Use `.isA<RegExp>()` type refinement along with cascades to
|
||||
assert on the properties of the `RegExp` object explicitly:
|
||||
```dart
|
||||
check(myPattern).isA<RegExp>()
|
||||
..has((r) => r.pattern, 'pattern').equals('Hello')
|
||||
..has((r) => r.isMultiLine, 'isMultiLine').isTrue();
|
||||
```
|
||||
|
||||
### 7. Strict Nullable Boolean Safety (`bool?` fields)
|
||||
- **Legacy Matcher**: Statically, `isTrue` and `isFalse` performed loose
|
||||
dynamic checks at runtime, which silently accepted nullable booleans (`bool?`).
|
||||
- **Package Checks**: `.isTrue()` and `.isFalse()` are defined strictly on
|
||||
`Subject<bool>` (non-nullable). They are **not** available on `Subject<bool?>`.
|
||||
- **Remediation**: For fields declared as `bool?`, you must either refine the
|
||||
subject (e.g., `.isNotNull().isTrue()`) or simply use `.equals(true)` and
|
||||
`.equals(false)` which are generic and work on all types:
|
||||
```dart
|
||||
// If options.flagOutdated is a bool?
|
||||
check(options.flagOutdated).equals(true);
|
||||
check(options.flagOutdated).equals(false);
|
||||
```
|
||||
|
||||
### 8. Map Key Containment (`containsKey` vs `contains`)
|
||||
- **Legacy Matcher**: In `package:matcher`, `contains(key)` was used to assert
|
||||
that a `Map` contained a specific key.
|
||||
- **Package Checks**: Calling `.contains(...)` on a `Subject<Map>` is not
|
||||
defined and will fail compilation.
|
||||
- **Remediation**: Use the map-specific `.containsKey(key)` matcher instead:
|
||||
```dart
|
||||
// BEFORE (Matcher)
|
||||
expect(myMap, contains('my_key'));
|
||||
|
||||
// AFTER (Checks)
|
||||
check(myMap).containsKey('my_key');
|
||||
```
|
||||
|
||||
### 9. Explicit Generic Parameters for Extension Types
|
||||
- **Legacy Matcher**: `expect(extensionTypeConst, 3)` compiled because of loose
|
||||
dynamic equality.
|
||||
- **Package Checks**: If `QrEciValue` is an extension type representation of `int`
|
||||
(e.g., `extension type const QrEciValue(int value) implements int`), calling
|
||||
`.equals(3)` on a `Subject<QrEciValue>` fails because `3` (an `int`) is not
|
||||
assignable to `QrEciValue`. Casting with `as int` will trigger an
|
||||
"Unnecessary cast" static analysis warning because `QrEciValue` statically
|
||||
implements `int`.
|
||||
- **Remediation**: Explicitly specify the generic type parameter on the `check`
|
||||
function to force checks to treat it as the primitive type:
|
||||
```dart
|
||||
// YES (Type-safe and warning-free)
|
||||
check<int>(QrEciValue.iso8859_1).equals(3);
|
||||
```
|
||||
|
||||
### 10. Dynamic Map / JSON Lookup Casting
|
||||
- **Legacy Matcher**: Loose dynamic typing allowed comparing nested json lookups
|
||||
statically typed as `dynamic` directly against lists or maps.
|
||||
- **Package Checks**: Strict type safety rejects the implicit assignment of
|
||||
`dynamic` to `Iterable<Object?>` in `.deepEquals(...)`.
|
||||
- **Remediation**: Statically cast the dynamic lookup result to a `List` or `Map`:
|
||||
```dart
|
||||
// YES (Explicit cast to List)
|
||||
check(myIterable).deepEquals(json['data']['items'] as List);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Matcher-to-Checks Mapping Table
|
||||
|
||||
Use this table as a quick reference for direct matcher replacements:
|
||||
|
||||
| Legacy Matcher | Package Checks Equivalent | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| `expect(actual, expected)` | `check(actual).equals(expected)` | Use `.deepEquals` for collections! |
|
||||
| `expect(actual, equals(expected))` | `check(actual).equals(expected)` | Use `.deepEquals` for collections! |
|
||||
| `isA<T>()` | `check(actual).isA<T>()` | Chaining is supported directly |
|
||||
| `same(expected)` | `check(actual).identicalTo(expected)` | Verifies identity |
|
||||
| `anyElement(matcher)` | `check(iterable).any(conditionCallback)` | E.g. `check(list).any((e) => e.equals(1))` |
|
||||
| `everyElement(matcher)` | `check(iterable).every(conditionCallback)` | E.g. `check(list).every((e) => e.isGreaterThan(0))` |
|
||||
| `hasLength(expected)` | `check(actual).length.equals(expected)` | Works on String, Map, Iterable, etc. |
|
||||
| `isNot(matcher)` | `check(actual).not(conditionCallback)` | E.g. `check(val).not((it) => it.equals(5))` |
|
||||
| `contains(element)` | `check(actual).contains(element)` | Works on String, Iterable (use `containsKey` for Map!) |
|
||||
| `contains(key)` (on a Map) | `check(map).containsKey(key)` | Map key containment |
|
||||
| `startsWith(prefix)` | `check(string).startsWith(prefix)` | String only |
|
||||
| `endsWith(suffix)` | `check(string).endsWith(suffix)` | String only |
|
||||
| `isEmpty` | `check(actual).isEmpty()` | Works on String, Map, Iterable |
|
||||
| `isNotEmpty` | `check(actual).isNotEmpty()` | Works on String, Map, Iterable |
|
||||
| `isNull` | `check(actual).isNull()` | |
|
||||
| `isNotNull` | `check(actual).isNotNull()` | |
|
||||
| `isTrue` / `true` | `check(actual).isTrue()` | Works on non-nullable `bool` only |
|
||||
| `isFalse` / `false` | `check(actual).isFalse()` | Works on non-nullable `bool` only |
|
||||
| `completion(matcher)` | `await check(future).completes(conditionCallback)` | Must be awaited! |
|
||||
| `throwsA(matcher)` | `await check(future).throws<Type>()` | Must be awaited! |
|
||||
| `emits(value)` | `await check(streamQueue).emits(conditionCallback)` | Must be awaited! |
|
||||
| `emitsThrough(value)` | `await check(streamQueue).emitsThrough(conditionCallback)` | Must be awaited! |
|
||||
| `stringContainsInOrder(list)` | `check(string).containsInOrder(list)` | String only |
|
||||
| `pairwiseCompare(...)` | `check(actual).pairwiseMatches(...)` | |
|
||||
|
||||
---
|
||||
|
||||
## Matchers with No Direct Replacements
|
||||
|
||||
Some legacy matchers do not have a one-to-one equivalent in `package:checks`
|
||||
due to API cleanup. Use these standard workarounds:
|
||||
|
||||
### 1. Specific Error Matchers
|
||||
- **Legacy**: `throwsArgumentError`, `throwsStateError`,
|
||||
`throwsUnsupportedError`, etc.
|
||||
- **Checks**: Use `.throws<T>()` with the specific error type:
|
||||
```dart
|
||||
await check(triggerError()).throws<ArgumentError>();
|
||||
```
|
||||
|
||||
### 2. The `anything` Matcher
|
||||
- **Legacy**: `expect(actual, anything)`
|
||||
- **Checks**: Pass an empty condition callback `(_) {}` when a condition is
|
||||
syntactically required:
|
||||
```dart
|
||||
await check(someFuture).completes((_) {});
|
||||
```
|
||||
|
||||
### 3. Specific Numeric Toggles
|
||||
- **Legacy**: `isPositive`, `isNegative`, `isZero`, `isNonPositive`,
|
||||
`isNonNegative`, `isNonZero`
|
||||
- **Checks**: Use explicit comparative expectations:
|
||||
- `isPositive` $\rightarrow$ `isGreaterThan(0)`
|
||||
- `isNegative` $\rightarrow$ `isLessThan(0)`
|
||||
- `isZero` $\rightarrow$ `equals(0)`
|
||||
- `isNonNegative` $\rightarrow$ `isGreaterOrEqual(0)`
|
||||
|
||||
### 4. Numeric Ranges
|
||||
- **Legacy**: `inClosedOpenRange(min, max)`, `inInclusiveRange(min, max)`,
|
||||
etc.
|
||||
- **Checks**: Chain the boundaries using the cascade operator (`..`):
|
||||
```dart
|
||||
check(actualValue)
|
||||
..isGreaterOrEqual(min)
|
||||
..isLessThan(max);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Writing Custom Expectations (Replacing Custom Matchers)
|
||||
|
||||
When migrating from a legacy codebase, you may encounter custom `Matcher`
|
||||
subclasses. In `package:checks`, custom assertions are implemented as
|
||||
`extension` methods on `Subject<T>`.
|
||||
|
||||
To write custom expectations, you must import the checks context API:
|
||||
```dart
|
||||
import 'package:checks/context.dart';
|
||||
```
|
||||
|
||||
### 1. Simple Custom Expectations (using `expect`)
|
||||
Use `context.expect` to check a property and return a `Rejection` on failure:
|
||||
```dart
|
||||
extension CustomPersonChecks on Subject<Person> {
|
||||
void isAdult() {
|
||||
context.expect(
|
||||
() => ['is an adult (age >= 18)'],
|
||||
(actual) {
|
||||
if (actual.age >= 18) return null; // Pass
|
||||
return Rejection(
|
||||
which: ['is only ${actual.age} years old'],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Nested Property Extraction (using `nest` or `has`)
|
||||
To extract a property and allow further chained checks, use `nest` or the
|
||||
simpler `has` helper:
|
||||
- **Using `has` (Recommended for simple, non-failing field access)**:
|
||||
```dart
|
||||
extension CustomPersonChecks on Subject<Person> {
|
||||
Subject<Address> get address => has((p) => p.address, 'address');
|
||||
}
|
||||
```
|
||||
- **Using `nest` (For property extraction that can fail or reject)**:
|
||||
```dart
|
||||
extension CustomPersonChecks on Subject<Person> {
|
||||
Subject<String> get ssn => context.nest(
|
||||
'has a valid SSN',
|
||||
(actual) {
|
||||
final ssnValue = actual.ssn;
|
||||
if (ssnValue == null) {
|
||||
return Extracted.rejection(which: ['has no SSN']);
|
||||
}
|
||||
return Extracted.value(ssnValue);
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Asynchronous Custom Expectations
|
||||
If the expectation is asynchronous (e.g. checking a Future or Stream), use
|
||||
`context.expectAsync` or `context.nestAsync` and return the resulting `Future`:
|
||||
```dart
|
||||
extension CustomFutureChecks<T> on Subject<Future<T>> {
|
||||
Future<void> completesNormally() {
|
||||
return context.expectAsync(
|
||||
() => ['completes without throwing'],
|
||||
(actual) async {
|
||||
try {
|
||||
await actual;
|
||||
return null; // Pass
|
||||
} catch (e) {
|
||||
return Rejection(which: ['threw $e']);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Strategies for Discovery
|
||||
|
||||
Execute these commands in the terminal to identify legacy matchers and files
|
||||
requiring migration:
|
||||
|
||||
```bash
|
||||
# 1. Find all test files containing legacy expect() or expectLater()
|
||||
grep -rn "expect(" test/
|
||||
grep -rn "expectLater(" test/
|
||||
|
||||
# 2. Find potential collection equality pitfalls (literal lists or maps)
|
||||
grep -rn "expect(.*, \[" test/
|
||||
grep -rn "expect(.*, {" test/
|
||||
|
||||
# 3. Find matches() calls (need conversion to RegExp + matchesPattern)
|
||||
grep -rn "matches(" test/
|
||||
|
||||
# 4. Find legacy TypeMatcher.having() calls (which need conversion to .has())
|
||||
grep -rn "having(" test/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Assertions
|
||||
**Before (Matcher):**
|
||||
```dart
|
||||
expect(someValue, isNotNull);
|
||||
expect(result, isTrue, reason: 'should be successful');
|
||||
expect(myString, startsWith('hello'));
|
||||
```
|
||||
|
||||
**After (Checks):**
|
||||
```dart
|
||||
check(someValue).isNotNull();
|
||||
check(because: 'should be successful', result).isTrue();
|
||||
check(myString).startsWith('hello');
|
||||
```
|
||||
|
||||
### Collection and Deep Equality
|
||||
**Before (Matcher):**
|
||||
```dart
|
||||
expect(items, [1, 2, 3]);
|
||||
expect(configMap, equals({'port': 8080}));
|
||||
```
|
||||
|
||||
**After (Checks):**
|
||||
```dart
|
||||
check(items).deepEquals([1, 2, 3]);
|
||||
check(configMap).deepEquals({'port': 8080});
|
||||
```
|
||||
|
||||
### Chaining and Cascades
|
||||
**Before (Matcher):**
|
||||
```dart
|
||||
expect(someString, allOf([
|
||||
startsWith('a'),
|
||||
contains('b'),
|
||||
endsWith('c'),
|
||||
]));
|
||||
```
|
||||
|
||||
**After (Checks):**
|
||||
```dart
|
||||
check(someString)
|
||||
..startsWith('a')
|
||||
..contains('b')
|
||||
..endsWith('c');
|
||||
```
|
||||
|
||||
### Complex Property Matching (has)
|
||||
**Before (Matcher):**
|
||||
```dart
|
||||
expect(response, isA<Response>()
|
||||
.having((r) => r.statusCode, 'statusCode', 200)
|
||||
.having((r) => r.body, 'body', contains('success')));
|
||||
```
|
||||
|
||||
**After (Checks):**
|
||||
```dart
|
||||
check(response).isA<Response>()
|
||||
..has((r) => r.statusCode, 'statusCode').equals(200)
|
||||
..has((r) => r.body, 'body').contains('success');
|
||||
```
|
||||
|
||||
### Asynchronous Futures
|
||||
**Before (Matcher):**
|
||||
```dart
|
||||
expect(fetchData(), completes);
|
||||
expect(fetchData(), completion(equals('data')));
|
||||
expect(failingCall(), throwsA(isA<StateError>()));
|
||||
```
|
||||
|
||||
**After (Checks):**
|
||||
```dart
|
||||
await check(fetchData()).completes();
|
||||
await check(fetchData()).completes((it) => it.equals('data'));
|
||||
await check(failingCall()).throws<StateError>();
|
||||
```
|
||||
|
||||
### Asynchronous Streams
|
||||
**Before (Matcher):**
|
||||
```dart
|
||||
var queue = StreamQueue(Stream.fromIterable([1, 2, 3]));
|
||||
await expectLater(queue, emitsInOrder([1, 2, 3]));
|
||||
```
|
||||
|
||||
**After (Checks):**
|
||||
```dart
|
||||
var queue = StreamQueue(Stream.fromIterable([1, 2, 3]));
|
||||
await check(queue).inOrder([
|
||||
(s) => s.emits((e) => e.equals(1)),
|
||||
(s) => s.emits((e) => e.equals(2)),
|
||||
(s) => s.emits((e) => e.equals(3)),
|
||||
]);
|
||||
```
|
||||
Reference in New Issue
Block a user