avoid_misused_test_matchers
v0.4.0 Warning Testing Rules
Flags an expect() call whose matcher cannot possibly agree with the actual value’s static type — isNull on a non-nullable int, isEmpty on a number, isList on a String.
A mismatched matcher does not fail loudly. Half of them always fail, so the assertion is useless; the other half always pass, so the test looks like coverage while asserting nothing.
Each of these is decided by the type before the test even runs:
void main() { test('reports a total', () { final total = 42;
expect(total, isNotNull); // always true — `int` cannot be null expect(total, isNull); // always false — same reason expect(total, isEmpty); // `int` has no isEmpty expect(total, hasLength(1)); // `int` has no length expect(total, isTrue); // `int` is not a bool });}The isNotNull line is the dangerous one: it passes, so the test is green and
nobody looks at it again.
The mirror mistakes, on a String:
void main() { test('reports a label', () { final label = 'hello';
expect(label, isList); // String is not a List expect(label, isMap); // String is not a Map expect(label, isZero); // String is not a num });}Assert the thing the type can actually be:
void main() { test('reports a total', () { final total = 42;
expect(total, equals(42)); expect(total, isPositive); expect(total, isNot(isZero)); });
test('reports a label', () { final label = 'hello';
expect(label, isNotEmpty); expect(label, hasLength(5)); expect(label, equals('hello')); });}Nullability matchers need a nullable type
Section titled “Nullability matchers need a nullable type”isNull and isNotNull are only meaningful where null is possible:
void main() { test('returns null for a missing key', () { // Don't — `int` is non-nullable, so this can never be null. final present = 1; expect(present, isNull);
// Do — the type admits null, so the assertion says something. final int? missing = null; expect(missing, isNull); });}The pairings the rule knows
Section titled “The pairings the rule knows”| Matcher | Actual value must be |
|---|---|
isNull, isNotNull |
a nullable type |
isEmpty, isNotEmpty |
String, Iterable or Map |
hasLength(n) |
String, Iterable or Map |
isList |
a List |
isMap |
a Map |
isZero, isNaN, isPositive, isNegative |
a num |
isTrue, isFalse |
a bool |
Anything not in this table — equals, contains, isA<T>, throwsA, a
matcher of your own — is never reported.
Never reported
Section titled “Never reported”A value whose static type is dynamic is never reported: the rule cannot know
what it holds, and guessing would produce false positives on exactly the tests
that need the flexibility.
void main() { test('accepts a dynamic payload', () { final dynamic payload = 42; expect(payload, isEmpty); // not reported — type is dynamic });}See also: test package - Matchers
Configuration
Section titled “Configuration”This rule is in the core preset, so it is on with preset: core,
preset: recommended or preset: opinionated.
To turn it off:
rules: avoid_misused_test_matchers: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”prefer_test_matchers— Prefer using a Matcher instead of a literal value in expect().prefer_expect_later— Use ‘expectLater’ instead of ‘expect’ when testing Futures.format_test_name— Hold test descriptions to a house pattern.prefer_correct_test_file_name— Name test files so the runner actually runs them.