Throw an error if an arg matcher is used improperly; 4.1.2 (#216)
Throw an error if an arg matcher is used improperly
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5f99597..58dc8e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 4.1.2
+
+* Produce a meaningful error message if an argument matcher is used outside of
+ stubbing (`when`) or verification (`verify` and `untilCalled`).
+
## 4.1.1
* Mark the unexported and accidentally public `setDefaultResponse` as
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 1cb9f53..bbe63be 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -4,6 +4,12 @@
implicit-casts: false
implicit-dynamic: false
errors:
+ # These are for 2.5.0-dev.2.0 and after
+ implicit_dynamic_list_literal: ignore
+ implicit_dynamic_map_literal: ignore
+ implicit_dynamic_parameter: ignore
+ implicit_dynamic_variable: ignore
+ # These are pre-2.5.0-dev.2.0
strong_mode_implicit_dynamic_list_literal: ignore
strong_mode_implicit_dynamic_map_literal: ignore
strong_mode_implicit_dynamic_parameter: ignore
diff --git a/lib/src/mock.dart b/lib/src/mock.dart
index 11db403..e8c6c46 100644
--- a/lib/src/mock.dart
+++ b/lib/src/mock.dart
@@ -680,30 +680,33 @@
}
/// An argument matcher that matches any argument passed in "this" position.
-Null get any => _registerMatcher(anything, false);
+Null get any => _registerMatcher(anything, false, argumentMatcher: 'any');
/// An argument matcher that matches any named argument passed in for the
/// parameter named [named].
-Null anyNamed(String named) => _registerMatcher(anything, false, named: named);
+Null anyNamed(String named) => _registerMatcher(anything, false,
+ named: named, argumentMatcher: 'anyNamed');
/// An argument matcher that matches any argument passed in "this" position, and
/// captures the argument for later access with `captured`.
-Null get captureAny => _registerMatcher(anything, true);
+Null get captureAny =>
+ _registerMatcher(anything, true, argumentMatcher: 'captureAny');
/// An argument matcher that matches any named argument passed in for the
/// parameter named [named], and captures the argument for later access with
/// `captured`.
-Null captureAnyNamed(String named) =>
- _registerMatcher(anything, true, named: named);
+Null captureAnyNamed(String named) => _registerMatcher(anything, true,
+ named: named, argumentMatcher: 'captureAnyNamed');
/// An argument matcher that matches an argument that matches [matcher].
Null argThat(Matcher matcher, {String named}) =>
- _registerMatcher(matcher, false, named: named);
+ _registerMatcher(matcher, false, named: named, argumentMatcher: 'argThat');
/// An argument matcher that matches an argument that matches [matcher], and
/// captures the argument for later access with `captured`.
Null captureThat(Matcher matcher, {String named}) =>
- _registerMatcher(matcher, true, named: named);
+ _registerMatcher(matcher, true,
+ named: named, argumentMatcher: 'captureThat');
@Deprecated('ArgMatchers no longer need to be wrapped in Mockito 3.0')
Null typed<T>(ArgMatcher matcher, {String named}) => null;
@@ -716,7 +719,27 @@
Null typedCaptureThat(Matcher matcher, {String named}) =>
captureThat(matcher, named: named);
-Null _registerMatcher(Matcher matcher, bool capture, {String named}) {
+/// Registers [matcher] into the stored arguments collections.
+///
+/// Creates an [ArgMatcher] with [matcher] and [capture], then if [named] is
+/// non-null, stores that into the positional stored arguments list; otherwise
+/// stores it into the named stored arguments map, keyed on [named].
+/// [argumentMatcher] is the name of the public API used to register [matcher],
+/// for error messages.
+Null _registerMatcher(Matcher matcher, bool capture,
+ {String named, String argumentMatcher}) {
+ if (!_whenInProgress && !_untilCalledInProgress && !_verificationInProgress) {
+ // It is not meaningful to store argument matchers outside of stubbing
+ // (`when`), or verification (`verify` and `untilCalled`). Such argument
+ // matchers will be processed later erroneously.
+ _storedArgs.clear();
+ _storedNamedArgs.clear();
+ throw ArgumentError(
+ 'The "$argumentMatcher" argument matcher is used outside of method '
+ 'stubbing (via `when`) or verification (via `verify` or `untilCalled`). '
+ 'This is invalid, and results in bad behavior during the next stubbing '
+ 'or verification.');
+ }
var argMatcher = ArgMatcher(matcher, capture);
if (named == null) {
_storedArgs.add(argMatcher);
diff --git a/pubspec.yaml b/pubspec.yaml
index b291b6d..affbad8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: mockito
-version: 4.1.1
+version: 4.1.2
authors:
- Dmitriy Fibulwinter <fibulwinter@gmail.com>
diff --git a/test/mockito_test.dart b/test/mockito_test.dart
index ff872f4..99da123 100644
--- a/test/mockito_test.dart
+++ b/test/mockito_test.dart
@@ -296,4 +296,10 @@
expect(() => mock.methodWithoutArgs(), returnsNormally);
});
});
+
+ test(
+ "reports an error when using an argument matcher outside of stubbing or "
+ "verification", () {
+ expect(() => mock.methodWithNormalArgs(any), throwsArgumentError);
+ });
}