- Skill ID
- flutter/agent-plugins/dart-migrate-to-checks-package
- Publisher
- flutter
- Repository
- agent-plugins
- Installs
- 204
- Files
- 1
- Synced
- Sep 24, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
flutter/agent-plugins/dart-migrate-to-checks-packageInstalls these files- SKILL.md
What this skill tells the agent
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
- How to Use This Skill (The Workflow)
- Key Syntax Differences and Pitfalls
- Matcher-to-Checks Mapping Table
- Matchers with No Direct Replacements
- Strategies for Discovery
- 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:checksas adev_dependencyinpubspec.yaml: ``bash dart pub add dev:checks`` - Remove
package:matcherif it is explicitly listed underdev_dependencies(it is typically transitively included bypackage:test, which is fine).
2. Identify and Plan Target Files
- Use the grep patterns in Strategies for Discovery to locate all test files containing legacy
expectorexpectLatercalls. - Decide whether to migrate files fully or incrementally.
3. Migrating a File (Incremental or Full)
For any target test file:
- 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()``
- Translate Assertions: Rewrite legacy
expectandexpectLatercalls tochecksyntax following the Key Syntax Differences and Pitfalls and the Matcher-to-Checks Mapping Table. - Verify via Compiler: If migrating fully, remove the
import 'package:test/expect.dart';line. Any remaining un-migratedexpectcalls 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 forunawaited_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 ofpackage: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)orexpect(actual, equals(expected))performed a deep equality check if the arguments were collections (Lists, Maps, Sets). - Package Checks:
.equals(expected)corresponds strictly tooperator ==. Since Dart collections do not overrideoperator ==for element-wise comparison, using.equalson 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`:expect(actual, expectation, reason: 'Explanation');
- **Package Checks**: The explanation is passed as the named argument
`because` to the `check` function *before* the actual subject: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:// 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)`: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: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>`:// 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:// 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:check(myPattern).isA<RegExp>() ..has((r) => r.pattern, 'pattern').equals('Hello')
More from flutter
Use this skill in a project
Open any RiverX project, search the Skills panel for flutter/agent-plugins/dart-migrate-to-checks-package, and install it in one click.
