Skip to content

require_mirror_test

v1.1.0WarningConfigurableTesting Rules

Warns when a library under lib/ has no matching test file:

lib/src/core/version.dart -> test/src/core/version_test.dart
lib/parser.dart -> test/parser_test.dart

The message says a test file is missing, not that the code is untested — the rule cannot tell the difference.

This rule is in no preset. Whether a project mirrors its test tree is a project decision, so it must be enabled by name.

analysis_options.yaml
many_lints:
rules:
require_mirror_test: true
// lib/src/core/version.dart — with no test/src/core/version_test.dart
class Version {
const Version(this.major, this.minor);
final int major;
final int minor;
}
lib/src/core/version.dart
class Version {
const Version(this.major, this.minor);
final int major;
final int minor;
}
test/src/core/version_test.dart
void main() {
test('parses a two-part version', () {
expect(Version.parse('1.2'), equals(const Version(1, 2)));
});
}
require_mirror_test: true
lib/parser.dart -> test/parser_test.dart
lib/src/core/version.dart -> test/src/core/version_test.dart
lib/src/net/http_client.dart -> test/src/net/http_client_test.dart

Both the directory and the suffix are configurable — a package using spec/ and _spec.dart names them:

analysis_options.yaml
many_lints:
rules:
require_mirror_test:
test_dir: spec
suffix: _spec
lib/src/core/version.dart -> spec/src/core/version_spec.dart

By default a file of the right name satisfies the rule wherever it sits under the test directory, so reorganising the test tree does not light up the whole package:

fallback_anywhere: true # the default
lib/src/core/version.dart
satisfied by test/src/core/version_test.dart (the mirrored path)
satisfied by test/unit/version_test.dart (found by the fallback)

Set it to false when the exact mirrored path is itself the convention you want enforced:

analysis_options.yaml
many_lints:
rules:
require_mirror_test:
fallback_anywhere: false
lib/src/core/version.dart
satisfied by test/src/core/version_test.dart
NOT satisfied by test/unit/version_test.dart // LINT

exclude takes globs. Public barrels at the root of lib/, or a directory of generated adapters, are typical exemptions:

analysis_options.yaml
many_lints:
rules:
require_mirror_test:
exclude:
- 'lib/*.dart'
- 'lib/src/adapters/**'

These need no configuration:

// lib/src/models/user.g.dart — generated, skipped
// lib/generated/l10n.dart — under generated/, skipped
// lib/src/models/models.dart — a barrel: exports only, so nothing to test
export 'user.dart';
export 'order.dart';
// lib/src/util/_helpers.dart — nothing public, so no surface a test can target
String _slugify(String value) => value.toLowerCase();
// lib/src/core/version_impl.dart — a part file; its library is checked instead
part of 'version.dart';

A file that both exports and declares is not a barrel — it has code of its own, so it is still reported.

Option Type Default Description
test_dir string test The directory the mirrored path is rooted at
suffix string _test Appended to the file’s base name
fallback_anywhere bool true Accept a file of the right name anywhere under test_dir

It checks for a file, not for tests. An empty version_test.dart satisfies the rule. Coverage tooling answers the other half.

Only lib/. Code under bin/, tool/ or example/ is never reported.

Names, not contents, under fallback_anywhere. With the default on, any version_test.dart anywhere under test/ satisfies every version.dart in the package, including one in a different directory.

many_lints.yaml
rules:
require_mirror_test: false

To keep the rule on but skip certain paths, use per-rule exclude.