Add scaffolding of code-generation API for Mockito.
PiperOrigin-RevId: 279997785
diff --git a/README.md b/README.md
index 09b6878..5b70df8 100644
--- a/README.md
+++ b/README.md
@@ -65,7 +65,7 @@
// We can calculate a response at call time.
var responses = ["Purr", "Meow"];
-when(cat.sound()).thenAnswer((_) => responses.removeAt(0));
+when(cat.sound()).thenAnswer(() => responses.removeAt(0));
expect(cat.sound(), "Purr");
expect(cat.sound(), "Meow");
```
diff --git a/bin/codegen.dart b/bin/codegen.dart
new file mode 100644
index 0000000..f3ec2b5
--- /dev/null
+++ b/bin/codegen.dart
@@ -0,0 +1,19 @@
+// Copyright 2019 Dart Mockito authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'package:mockito/src/builder.dart';
+
+import 'package:build/build.dart';
+
+Builder buildMocks(BuilderOptions options) => MockBuilder();
diff --git a/build.yaml b/build.yaml
new file mode 100644
index 0000000..755c286
--- /dev/null
+++ b/build.yaml
@@ -0,0 +1,6 @@
+builders:
+ mockBuilder:
+ import: "package:mockito/src/builder.dart"
+ builder_factories: ["buildMocks"]
+ build_extensions: {".dart": [".mocks.dart"]}
+ build_to: source
diff --git a/example/example.dart b/example/example.dart
index 3a708b5..9742c3a 100644
--- a/example/example.dart
+++ b/example/example.dart
@@ -5,9 +5,9 @@
// Real class
class Cat {
- String sound() => 'Meow';
+ String sound() => "Meow";
bool eatFood(String food, {bool hungry}) => true;
- Future<void> chew() async => print('Chewing...');
+ Future<void> chew() async => print("Chewing...");
int walk(List<String> places) => 7;
void sleep() {}
void hunt(String place, String prey) {}
@@ -42,20 +42,20 @@
verify(cat.sound());
});
- test('How about some stubbing?', () {
+ test("How about some stubbing?", () {
// Unstubbed methods return null.
expect(cat.sound(), null);
// Stub a method before interacting with it.
- when(cat.sound()).thenReturn('Purr');
- expect(cat.sound(), 'Purr');
+ when(cat.sound()).thenReturn("Purr");
+ expect(cat.sound(), "Purr");
// You can call it again.
- expect(cat.sound(), 'Purr');
+ expect(cat.sound(), "Purr");
// Let's change the stub.
- when(cat.sound()).thenReturn('Meow');
- expect(cat.sound(), 'Meow');
+ when(cat.sound()).thenReturn("Meow");
+ expect(cat.sound(), "Meow");
// You can stub getters.
when(cat.lives).thenReturn(9);
@@ -66,48 +66,48 @@
expect(() => cat.lives, throwsRangeError);
// We can calculate a response at call time.
- var responses = ['Purr', 'Meow'];
+ var responses = ["Purr", "Meow"];
when(cat.sound()).thenAnswer((_) => responses.removeAt(0));
- expect(cat.sound(), 'Purr');
- expect(cat.sound(), 'Meow');
+ expect(cat.sound(), "Purr");
+ expect(cat.sound(), "Meow");
});
- test('Argument matchers', () {
+ test("Argument matchers", () {
// You can use plain arguments themselves
- when(cat.eatFood('fish')).thenReturn(true);
+ when(cat.eatFood("fish")).thenReturn(true);
// ... including collections
- when(cat.walk(['roof', 'tree'])).thenReturn(2);
+ when(cat.walk(["roof", "tree"])).thenReturn(2);
// ... or matchers
- when(cat.eatFood(argThat(startsWith('dry')))).thenReturn(false);
+ when(cat.eatFood(argThat(startsWith("dry")))).thenReturn(false);
// ... or mix aguments with matchers
- when(cat.eatFood(argThat(startsWith('dry')), hungry: true))
+ when(cat.eatFood(argThat(startsWith("dry")), hungry: true))
.thenReturn(true);
- expect(cat.eatFood('fish'), isTrue);
- expect(cat.walk(['roof', 'tree']), equals(2));
- expect(cat.eatFood('dry food'), isFalse);
- expect(cat.eatFood('dry food', hungry: true), isTrue);
+ expect(cat.eatFood("fish"), isTrue);
+ expect(cat.walk(["roof", "tree"]), equals(2));
+ expect(cat.eatFood("dry food"), isFalse);
+ expect(cat.eatFood("dry food", hungry: true), isTrue);
// You can also verify using an argument matcher.
- verify(cat.eatFood('fish'));
- verify(cat.walk(['roof', 'tree']));
- verify(cat.eatFood(argThat(contains('food'))));
+ verify(cat.eatFood("fish"));
+ verify(cat.walk(["roof", "tree"]));
+ verify(cat.eatFood(argThat(contains("food"))));
// You can verify setters.
cat.lives = 9;
verify(cat.lives = 9);
- cat.hunt('backyard', null);
- verify(cat.hunt('backyard', null)); // OK: no arg matchers.
+ cat.hunt("backyard", null);
+ verify(cat.hunt("backyard", null)); // OK: no arg matchers.
- cat.hunt('backyard', null);
- verify(cat.hunt(argThat(contains('yard')),
+ cat.hunt("backyard", null);
+ verify(cat.hunt(argThat(contains("yard")),
argThat(isNull))); // OK: null is wrapped in an arg matcher.
});
- test('Named arguments', () {
+ test("Named arguments", () {
// GOOD: argument matchers include their names.
when(cat.eatFood(any, hungry: anyNamed('hungry'))).thenReturn(true);
when(cat.eatFood(any, hungry: argThat(isNotNull, named: 'hungry')))
@@ -117,7 +117,7 @@
.thenReturn(true);
});
- test('Verifying exact number of invocations / at least x / never', () {
+ test("Verifying exact number of invocations / at least x / never", () {
cat.sound();
cat.sound();
// Exact number of invocations
@@ -133,41 +133,41 @@
verifyNever(cat.eatFood(any));
});
- test('Verification in order', () {
- cat.eatFood('Milk');
+ test("Verification in order", () {
+ cat.eatFood("Milk");
cat.sound();
- cat.eatFood('Fish');
- verifyInOrder([cat.eatFood('Milk'), cat.sound(), cat.eatFood('Fish')]);
+ cat.eatFood("Fish");
+ verifyInOrder([cat.eatFood("Milk"), cat.sound(), cat.eatFood("Fish")]);
});
- test('Making sure interaction(s) never happened on mock', () {
+ test("Making sure interaction(s) never happened on mock", () {
verifyZeroInteractions(cat);
});
- test('Finding redundant invocations', () {
+ test("Finding redundant invocations", () {
cat.sound();
verify(cat.sound());
verifyNoMoreInteractions(cat);
});
- test('Capturing arguments for further assertions', () {
+ test("Capturing arguments for further assertions", () {
// Simple capture:
- cat.eatFood('Fish');
- expect(verify(cat.eatFood(captureAny)).captured.single, 'Fish');
+ cat.eatFood("Fish");
+ expect(verify(cat.eatFood(captureAny)).captured.single, "Fish");
// Capture multiple calls:
- cat.eatFood('Milk');
- cat.eatFood('Fish');
- expect(verify(cat.eatFood(captureAny)).captured, ['Milk', 'Fish']);
+ cat.eatFood("Milk");
+ cat.eatFood("Fish");
+ expect(verify(cat.eatFood(captureAny)).captured, ["Milk", "Fish"]);
// Conditional capture:
- cat.eatFood('Milk');
- cat.eatFood('Fish');
+ cat.eatFood("Milk");
+ cat.eatFood("Fish");
expect(
- verify(cat.eatFood(captureThat(startsWith('F')))).captured, ['Fish']);
+ verify(cat.eatFood(captureThat(startsWith("F")))).captured, ["Fish"]);
});
- test('Waiting for an interaction', () async {
+ test("Waiting for an interaction", () async {
Future<void> chewHelper(Cat cat) {
return cat.chew();
}
@@ -177,15 +177,15 @@
await untilCalled(cat.chew()); // This completes when cat.chew() is called.
// Waiting for a call that has already happened.
- cat.eatFood('Fish');
+ cat.eatFood("Fish");
await untilCalled(cat.eatFood(any)); // This completes immediately.
});
- test('Fake class', () {
+ test("Fake class", () {
// Create a new fake Cat at runtime.
var cat = FakeCat();
- cat.eatFood('Milk'); // Prints 'Fake eat Milk'.
+ cat.eatFood("Milk"); // Prints 'Fake eat Milk'.
expect(() => cat.sleep(), throwsUnimplementedError);
});
}
diff --git a/example/iss/iss.dart b/example/iss/iss.dart
index eef8df4..e932fd9 100644
--- a/example/iss/iss.dart
+++ b/example/iss/iss.dart
@@ -31,7 +31,9 @@
/// Returns the current GPS position in [latitude, longitude] format.
Future update() async {
- _ongoingRequest ??= _doUpdate();
+ if (_ongoingRequest == null) {
+ _ongoingRequest = _doUpdate();
+ }
await _ongoingRequest;
_ongoingRequest = null;
}
@@ -39,7 +41,7 @@
Future _doUpdate() async {
// Returns the point on the earth directly under the space station
// at this moment.
- var rs = await client.get('http://api.open-notify.org/iss-now.json');
+ Response rs = await client.get('http://api.open-notify.org/iss-now.json');
var data = jsonDecode(rs.body);
var latitude = double.parse(data['iss_position']['latitude'] as String);
var longitude = double.parse(data['iss_position']['longitude'] as String);
@@ -58,7 +60,7 @@
// The ISS is defined to be visible if the distance from the observer to
// the point on the earth directly under the space station is less than 80km.
bool get isVisible {
- var distance = sphericalDistanceKm(locator.currentPosition, observer);
+ double distance = sphericalDistanceKm(locator.currentPosition, observer);
return distance < 80.0;
}
}
diff --git a/example/iss/iss_test.dart b/example/iss/iss_test.dart
index dc4f3cd..3aa2a62 100644
--- a/example/iss/iss_test.dart
+++ b/example/iss/iss_test.dart
@@ -27,18 +27,18 @@
// verify the calculated distance between them.
group('Spherical distance', () {
test('London - Paris', () {
- var london = Point(51.5073, -0.1277);
- var paris = Point(48.8566, 2.3522);
- var d = sphericalDistanceKm(london, paris);
+ Point<double> london = Point(51.5073, -0.1277);
+ Point<double> paris = Point(48.8566, 2.3522);
+ double d = sphericalDistanceKm(london, paris);
// London should be approximately 343.5km
// (+/- 0.1km) from Paris.
expect(d, closeTo(343.5, 0.1));
});
test('San Francisco - Mountain View', () {
- var sf = Point(37.783333, -122.416667);
- var mtv = Point(37.389444, -122.081944);
- var d = sphericalDistanceKm(sf, mtv);
+ Point<double> sf = Point(37.783333, -122.416667);
+ Point<double> mtv = Point(37.389444, -122.081944);
+ double d = sphericalDistanceKm(sf, mtv);
// San Francisco should be approximately 52.8km
// (+/- 0.1km) from Mountain View.
expect(d, closeTo(52.8, 0.1));
@@ -52,8 +52,8 @@
// second predefined location. This test runs asynchronously.
group('ISS spotter', () {
test('ISS visible', () async {
- var sf = Point(37.783333, -122.416667);
- var mtv = Point(37.389444, -122.081944);
+ Point<double> sf = Point(37.783333, -122.416667);
+ Point<double> mtv = Point(37.389444, -122.081944);
IssLocator locator = MockIssLocator();
// Mountain View should be visible from San Francisco.
when(locator.currentPosition).thenReturn(sf);
@@ -63,8 +63,8 @@
});
test('ISS not visible', () async {
- var london = Point(51.5073, -0.1277);
- var mtv = Point(37.389444, -122.081944);
+ Point<double> london = Point(51.5073, -0.1277);
+ Point<double> mtv = Point(37.389444, -122.081944);
IssLocator locator = MockIssLocator();
// London should not be visible from Mountain View.
when(locator.currentPosition).thenReturn(london);
diff --git a/lib/annotations.dart b/lib/annotations.dart
new file mode 100644
index 0000000..bf1f5da
--- /dev/null
+++ b/lib/annotations.dart
@@ -0,0 +1,19 @@
+// Copyright 2019 Dart Mockito authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+class GenerateMocks {
+ final List<Type> classes;
+
+ const GenerateMocks(this.classes);
+}
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
new file mode 100644
index 0000000..16bd59f
--- /dev/null
+++ b/lib/src/builder.dart
@@ -0,0 +1,228 @@
+// Copyright 2019 Dart Mockito authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:build/build.dart';
+import 'package:code_builder/code_builder.dart';
+import 'package:dart_style/dart_style.dart';
+
+/// For a source Dart library, generate the mocks referenced therein.
+///
+/// Given an input library, 'foo.dart', this builder will search the top-level
+/// elements for an annotation, `@GenerateMocks`, from the mockito package. For
+/// example:
+///
+/// ```dart
+/// @GenerateMocks[Foo]
+/// void main() {}
+/// ```
+///
+/// If this builder finds any classes to mock (for example, `Foo`, above), it
+/// will produce a "'.mocks.dart' file with such mocks. In this example,
+/// 'foo.mocks.dart' will be created.
+class MockBuilder implements Builder {
+ @override
+ Future build(BuildStep buildStep) async {
+ final entryLib = await buildStep.inputLibrary;
+ final resolver = buildStep.resolver;
+
+ final mockLibrary = buildStep.inputId.changeExtension('.mocks.dart');
+
+ final mockClasses = <Class>[];
+
+ for (final element in entryLib.topLevelElements) {
+ final annotation = element.metadata.firstWhere(
+ (annotation) =>
+ annotation.element is ConstructorElement &&
+ annotation.element.enclosingElement.name == 'GenerateMocks',
+ orElse: () => null);
+ if (annotation == null) continue;
+ final generateMocksValue = annotation.computeConstantValue();
+ // TODO(srawlins): handle `generateMocksValue == null`?
+ final classesToMock = generateMocksValue.getField('classes');
+ if (classesToMock.isNull)
+ // TODO(srawlins): Log severe instead? How? How to test?
+ throw StateError(
+ 'Unresolved "classes" argument for GenerateMocks has errors: '
+ '"${generateMocksValue}');
+ for (final classToMock in classesToMock.toListValue()) {
+ final dartTypeToMock = classToMock.toTypeValue();
+ // TODO(srawlins): Import the library which declares [dartTypeToMock].
+ // TODO(srawlins): Import all supporting libraries, used in type
+ // signatures.
+ _buildCodeForClass(dartTypeToMock, mockClasses);
+ }
+ }
+
+ if (mockClasses.isEmpty) {
+ // Nothing to mock here!
+ return;
+ }
+
+ final emitter = DartEmitter();
+ final mockLibraryContent = DartFormatter()
+ .format(mockClasses.map((c) => c.accept(emitter)).join('\n'));
+
+ await buildStep.writeAsString(mockLibrary, mockLibraryContent);
+ }
+
+ void _buildCodeForClass(final DartType dartType, List<Class> mockClasses) {
+ final elementToMock = dartType.element;
+ // TODO(srawlins): Log/throw here.
+ if (elementToMock is! ClassElement) return;
+ final classToMock = elementToMock as ClassElement;
+ final className = dartType.displayName;
+
+ // TODO(srawlins): Add a dartdoc to the Mock class.
+ final mockClass = Class((cBuilder) {
+ cBuilder
+ ..name = 'Mock$className'
+ ..extend = refer('Mock')
+ ..implements.add(refer(className))
+ ..docs.add('/// A class which mocks [$className].')
+ ..docs.add('///')
+ ..docs.add('/// See the documentation for Mockito\'s code generation '
+ 'for more information.');
+ for (final field in classToMock.fields) {
+ if (field.isPrivate || field.isStatic) {
+ continue;
+ }
+ // Handle getters when we handle non-nullable return types.
+ final setter = field.setter;
+ if (setter != null) {
+ cBuilder.methods.add(
+ Method((mBuilder) => _buildOverridingSetter(mBuilder, setter)));
+ }
+ }
+ for (final method in classToMock.methods) {
+ if (method.parameters.isEmpty || method.isPrivate || method.isStatic) {
+ continue;
+ }
+ cBuilder.methods.add(
+ Method((mBuilder) => _buildOverridingMethod(mBuilder, method)));
+ }
+ });
+
+ mockClasses.add(mockClass);
+ }
+
+ /// Build a method which overrides [method], with all non-nullable
+ /// parameter types widened to be nullable.
+ ///
+ /// This new method just calls `super.noSuchMethod`, optionally passing a
+ /// return value for methods with a non-nullable return type.
+ // TODO(srawlins): This method does no widening yet. Widen parameters. Include
+ // tests for typedefs, old-style function parameters, and function types.
+ // TODO(srawlins): This method declares no specific non-null return values
+ // yet.
+ void _buildOverridingMethod(MethodBuilder builder, MethodElement method) {
+ // TODO(srawlins): generator methods like async*, sync*.
+ // TODO(srawlins): abstract methods?
+ builder
+ ..name = method.displayName
+ ..returns = refer(method.returnType.displayName);
+
+ if (method.isAsynchronous) {
+ builder.modifier =
+ method.isGenerator ? MethodModifier.asyncStar : MethodModifier.async;
+ } else if (method.isGenerator) {
+ builder.modifier = MethodModifier.syncStar;
+ }
+
+ // These two variables store the arguments that will be passed to the
+ // [Invocation] built for `noSuchMethod`.
+ final invocationPositionalArgs = <Expression>[];
+ final invocationNamedArgs = <Expression, Expression>{};
+
+ for (final parameter in method.parameters) {
+ if (parameter.isRequiredPositional) {
+ builder.requiredParameters.add(_matchingParameter(parameter));
+ invocationPositionalArgs.add(refer(parameter.displayName));
+ } else if (parameter.isOptionalPositional) {
+ builder.optionalParameters.add(_matchingParameter(parameter));
+ invocationPositionalArgs.add(refer(parameter.displayName));
+ } else if (parameter.isNamed) {
+ builder.optionalParameters.add(_matchingParameter(parameter));
+ invocationNamedArgs[refer('#${parameter.displayName}')] =
+ refer(parameter.displayName);
+ }
+ }
+ // TODO(srawlins): Optionally pass a non-null return value to `noSuchMethod`
+ // which `Mock.noSuchMethod` will simply return, in order to satisfy runtime
+ // type checks.
+ // TODO(srawlins): Handle getter invocations with `Invocation.getter`,
+ // and operators???
+ // TODO(srawlins): Handle generic methods with `Invocation.genericMethod`.
+ final invocation = refer('Invocation').property('method').call([
+ refer('#${method.displayName}'),
+ literalList(invocationPositionalArgs),
+ if (invocationNamedArgs.isNotEmpty) literalMap(invocationNamedArgs),
+ ]);
+ final returnNoSuchMethod =
+ refer('super').property('noSuchMethod').call([invocation]);
+
+ builder.body = returnNoSuchMethod.code;
+ }
+
+ /// Returns a [Parameter] which matches [parameter].
+ Parameter _matchingParameter(ParameterElement parameter) =>
+ Parameter((pBuilder) {
+ pBuilder
+ ..name = parameter.displayName
+ ..type = refer(parameter.type.displayName);
+ if (parameter.isNamed) pBuilder.named = true;
+ if (parameter.defaultValueCode != null) {
+ pBuilder.defaultTo = Code(parameter.defaultValueCode);
+ }
+ });
+
+ /// Build a setter which overrides [setter], widening the single parameter
+ /// type to be nullable if it is non-nullable.
+ ///
+ /// This new setter just calls `super.noSuchMethod`.
+ // TODO(srawlins): This method does no widening yet.
+ void _buildOverridingSetter(
+ MethodBuilder builder, PropertyAccessorElement setter) {
+ builder
+ ..name = setter.displayName
+ ..type = MethodType.setter;
+
+ final invocationPositionalArgs = <Expression>[];
+ // There should only be one required positional parameter. Should we assert
+ // on that? Leave it alone?
+ for (final parameter in setter.parameters) {
+ if (parameter.isRequiredPositional) {
+ builder.requiredParameters.add(Parameter((pBuilder) => pBuilder
+ ..name = parameter.displayName
+ ..type = refer(parameter.type.displayName)));
+ invocationPositionalArgs.add(refer(parameter.displayName));
+ }
+ }
+
+ final invocation = refer('Invocation').property('setter').call([
+ refer('#${setter.displayName}'),
+ literalList(invocationPositionalArgs),
+ ]);
+ final returnNoSuchMethod =
+ refer('super').property('noSuchMethod').call([invocation]);
+
+ builder.body = returnNoSuchMethod.code;
+ }
+
+ @override
+ final buildExtensions = const {
+ '.dart': ['.mocks.dart']
+ };
+}
diff --git a/lib/src/mock.dart b/lib/src/mock.dart
index d83c070..320586e 100644
--- a/lib/src/mock.dart
+++ b/lib/src/mock.dart
@@ -17,7 +17,6 @@
import 'package:meta/meta.dart';
import 'package:mockito/src/call_pair.dart';
import 'package:mockito/src/invocation_matcher.dart';
-// ignore: deprecated_member_use
import 'package:test_api/test_api.dart';
// TODO(srawlins): Remove this when we no longer need to check for an
// incompatiblity between test_api and test.
@@ -38,8 +37,7 @@
@Deprecated(
'This function is not a supported function, and may be deleted as early as '
'Mockito 5.0.0')
-void setDefaultResponse(
- Mock mock, CallPair<dynamic> Function() defaultResponse) {
+void setDefaultResponse(Mock mock, CallPair<dynamic> defaultResponse()) {
mock._defaultResponse = defaultResponse;
}
@@ -138,7 +136,7 @@
const Object().noSuchMethod(invocation);
@override
- int get hashCode => _givenHashCode ?? 0;
+ int get hashCode => _givenHashCode == null ? 0 : _givenHashCode;
@override
bool operator ==(other) => (_givenHashCode != null && other is Mock)
@@ -146,7 +144,7 @@
: identical(this, other);
@override
- String toString() => _givenName ?? runtimeType.toString();
+ String toString() => _givenName != null ? _givenName : runtimeType.toString();
String _realCallsToString() {
var stringRepresentations = _realCalls.map((call) => call.toString());
@@ -240,7 +238,7 @@
factory _InvocationForMatchedArguments(Invocation invocation) {
if (_storedArgs.isEmpty && _storedNamedArgs.isEmpty) {
throw StateError(
- '_InvocationForMatchedArguments called when no ArgMatchers have been saved.');
+ "_InvocationForMatchedArguments called when no ArgMatchers have been saved.");
}
// Handle named arguments first, so that we can provide useful errors for
@@ -291,7 +289,7 @@
// Iterate through the stored named args, validate them, and add them to
// the return map.
_storedNamedArgs.forEach((name, arg) {
- var nameSymbol = Symbol(name);
+ Symbol nameSymbol = Symbol(name);
if (!invocation.namedArguments.containsKey(nameSymbol)) {
// Clear things out for the next call.
_storedArgs.clear();
@@ -346,8 +344,8 @@
'needs to specify the name of the argument it is being used in. For '
'example: `when(obj.fn(x: anyNamed("x")))`).');
}
- var storedIndex = 0;
- var positionalIndex = 0;
+ int storedIndex = 0;
+ int positionalIndex = 0;
while (storedIndex < _storedArgs.length &&
positionalIndex < invocation.positionalArguments.length) {
var arg = _storedArgs[storedIndex];
@@ -458,7 +456,7 @@
}
void _captureArguments(Invocation invocation) {
- var index = 0;
+ int index = 0;
for (var roleArg in roleInvocation.positionalArguments) {
var actArg = invocation.positionalArguments[index];
if (roleArg is ArgMatcher && roleArg._capture) {
@@ -486,7 +484,7 @@
roleInvocation.namedArguments.length) {
return false;
}
- var index = 0;
+ int index = 0;
for (var roleArg in roleInvocation.positionalArguments) {
var actArg = invocation.positionalArguments[index];
if (!isMatchingArg(roleArg, actArg)) {
@@ -543,7 +541,8 @@
@override
String toString() {
var argString = '';
- var args = invocation.positionalArguments.map((v) => '$v');
+ var args = invocation.positionalArguments
+ .map((v) => v == null ? "null" : v.toString());
if (args.any((arg) => arg.contains('\n'))) {
// As one or more arg contains newlines, put each on its own line, and
// indent each, for better readability.
@@ -650,18 +649,18 @@
if (!never && matchingInvocations.isEmpty) {
var message;
if (mock._realCalls.isEmpty) {
- message = 'No matching calls (actually, no calls at all).';
+ message = "No matching calls (actually, no calls at all).";
} else {
var otherCalls = mock._realCallsToString();
- message = 'No matching calls. All calls: $otherCalls';
+ message = "No matching calls. All calls: $otherCalls";
}
- fail('$message\n'
- '(If you called `verify(...).called(0);`, please instead use '
- '`verifyNever(...);`.)');
+ fail("$message\n"
+ "(If you called `verify(...).called(0);`, please instead use "
+ "`verifyNever(...);`.)");
}
if (never && matchingInvocations.isNotEmpty) {
var calls = mock._realCallsToString();
- fail('Unexpected calls. All calls: $calls');
+ fail("Unexpected calls. All calls: $calls");
}
matchingInvocations.forEach((inv) {
inv.verified = true;
@@ -874,7 +873,7 @@
_checkTestApiMismatch();
}
expect(callCount, wrapMatcher(matcher),
- reason: 'Unexpected number of calls');
+ reason: "Unexpected number of calls");
}
}
@@ -940,12 +939,12 @@
return <T>(T mock) {
_verificationInProgress = false;
if (_verifyCalls.length == 1) {
- var verifyCall = _verifyCalls.removeLast();
+ _VerifyCall verifyCall = _verifyCalls.removeLast();
var result = VerificationResult._(verifyCall.matchingInvocations.length);
verifyCall._checkWith(never);
return result;
} else {
- fail('Used on a non-mockito object');
+ fail("Used on a non-mockito object");
}
};
}
@@ -971,22 +970,23 @@
_verificationInProgress = true;
return <T>(List<T> _) {
_verificationInProgress = false;
- var dt = DateTime.fromMillisecondsSinceEpoch(0);
+ DateTime dt = DateTime.fromMillisecondsSinceEpoch(0);
var tmpVerifyCalls = List<_VerifyCall>.from(_verifyCalls);
_verifyCalls.clear();
- var matchedCalls = <RealCall>[];
- for (var verifyCall in tmpVerifyCalls) {
- var matched = verifyCall._findAfter(dt);
+ List<RealCall> matchedCalls = [];
+ for (_VerifyCall verifyCall in tmpVerifyCalls) {
+ RealCall matched = verifyCall._findAfter(dt);
if (matched != null) {
matchedCalls.add(matched);
dt = matched.timeStamp;
} else {
- var mocks = tmpVerifyCalls.map((vc) => vc.mock).toSet();
- var allInvocations =
+ Set<Mock> mocks =
+ tmpVerifyCalls.map((_VerifyCall vc) => vc.mock).toSet();
+ List<RealCall> allInvocations =
mocks.expand((m) => m._realCalls).toList(growable: false);
allInvocations
.sort((inv1, inv2) => inv1.timeStamp.compareTo(inv2.timeStamp));
- var otherCalls = '';
+ String otherCalls = "";
if (allInvocations.isNotEmpty) {
otherCalls = " All calls: ${allInvocations.join(", ")}";
}
@@ -1011,7 +1011,7 @@
if (mock is Mock) {
var unverified = mock._realCalls.where((inv) => !inv.verified).toList();
if (unverified.isNotEmpty) {
- fail('No more calls expected, but following found: ' + unverified.join());
+ fail("No more calls expected, but following found: " + unverified.join());
}
} else {
_throwMockArgumentError('verifyNoMoreInteractions', mock);
@@ -1021,7 +1021,7 @@
void verifyZeroInteractions(var mock) {
if (mock is Mock) {
if (mock._realCalls.isNotEmpty) {
- fail('No interaction expected, but following found: ' +
+ fail("No interaction expected, but following found: " +
mock._realCalls.join());
}
} else {
@@ -1084,7 +1084,7 @@
/// Print all collected invocations of any mock methods of [mocks].
void logInvocations(List<Mock> mocks) {
- var allInvocations =
+ List<RealCall> allInvocations =
mocks.expand((m) => m._realCalls).toList(growable: false);
allInvocations.sort((inv1, inv2) => inv1.timeStamp.compareTo(inv2.timeStamp));
allInvocations.forEach((inv) {
diff --git a/pubspec.yaml b/pubspec.yaml
index 1f438d9..521dd80 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: mockito
-version: 4.1.2-dev
+version: 4.1.1
authors:
- Dmitriy Fibulwinter <fibulwinter@gmail.com>
@@ -9,10 +9,14 @@
homepage: https://github.com/dart-lang/mockito
environment:
- sdk: '>=2.0.0 <3.0.0'
+ sdk: '>=2.3.0 <3.0.0'
dependencies:
+ analyzer: ^0.36.0
+ build: ^1.1.3
+ code_builder: ^3.2.0
collection: ^1.1.0
+ dart_style: ^1.2.5
matcher: ^0.12.3
meta: ^1.0.4
test_api: ^0.2.1
diff --git a/test/all.dart b/test/all.dart
index ef120e6..0aa5635 100644
--- a/test/all.dart
+++ b/test/all.dart
@@ -15,6 +15,7 @@
// This file explicitly does _not_ end in `_test.dart`, so that it is not picked
// up by `pub run test`. It is here for coveralls.
+import 'builder_test.dart' as builder_test;
import 'capture_test.dart' as capture_test;
import 'invocation_matcher_test.dart' as invocation_matcher_test;
import 'mockito_test.dart' as mockito_test;
@@ -22,6 +23,7 @@
import 'verify_test.dart' as verify_test;
void main() {
+ builder_test.main();
capture_test.main();
invocation_matcher_test.main();
mockito_test.main();
diff --git a/test/builder_test.dart b/test/builder_test.dart
new file mode 100644
index 0000000..c2a4224
--- /dev/null
+++ b/test/builder_test.dart
@@ -0,0 +1,266 @@
+// Copyright 2019 Dart Mockito authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'package:build/build.dart';
+import 'package:build_test/build_test.dart';
+import 'package:mockito/src/builder.dart';
+import 'package:test/test.dart';
+
+Builder buildMocks(BuilderOptions options) => MockBuilder();
+
+const annotationsAsset = {
+ 'mockito|lib/annotations.dart': '''
+class GenerateMocks {
+ final List<Type> classes;
+
+ const GenerateMocks(this.classes);
+}
+'''
+};
+
+const simpleTestAsset = {
+ 'foo|test/foo_test.dart': '''
+import 'package:foo/foo.dart';
+import 'package:mockito/annotations.dart';
+@GenerateMocks([Foo])
+void main() {}
+'''
+};
+
+void main() {
+ test(
+ 'generates mock for an imported class but does not override private '
+ 'or static methods or methods w/ zero parameters', () async {
+ await testBuilder(
+ buildMocks(BuilderOptions({})),
+ {
+ ...annotationsAsset,
+ ...simpleTestAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ class Foo {
+ int a() => 7;
+ int _b(int x) => 8;
+ static int c(int y) => 9;
+ }
+ '''),
+ },
+ outputs: {
+ 'foo|test/foo_test.mocks.dart': dedent(r'''
+ /// A class which mocks [Foo].
+ ///
+ /// See the documentation for Mockito's code generation for more information.
+ class MockFoo extends Mock implements Foo {}
+ '''),
+ },
+ );
+ });
+
+ test(
+ 'generates mock for an imported class but does not override private '
+ 'or static fields', () async {
+ await testBuilder(
+ buildMocks(BuilderOptions({})),
+ {
+ ...annotationsAsset,
+ ...simpleTestAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ class Foo {
+ int _a;
+ static int b;
+ }
+ '''),
+ },
+ outputs: {
+ 'foo|test/foo_test.mocks.dart': dedent(r'''
+ /// A class which mocks [Foo].
+ ///
+ /// See the documentation for Mockito's code generation for more information.
+ class MockFoo extends Mock implements Foo {}
+ '''),
+ },
+ );
+ });
+
+ test(
+ 'generates mock for an imported class but does not override any '
+ 'extension methods', () async {
+ await testBuilder(
+ buildMocks(BuilderOptions({})),
+ {
+ ...annotationsAsset,
+ ...simpleTestAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ extension X on Foo {
+ dynamic x(int m, String n) => n + 1;
+ }
+ class Foo {
+ dynamic a(int m, String n) => n + 1;
+ }
+ '''),
+ },
+ outputs: {
+ 'foo|test/foo_test.mocks.dart': dedent(r'''
+ /// A class which mocks [Foo].
+ ///
+ /// See the documentation for Mockito's code generation for more information.
+ class MockFoo extends Mock implements Foo {
+ dynamic a(int m, String n) =>
+ super.noSuchMethod(Invocation.method(#a, [m, n]));
+ }
+ '''),
+ },
+ );
+ });
+
+ test('generates a mock class and overrides methods parameters', () async {
+ await testBuilder(
+ buildMocks(BuilderOptions({})),
+ {
+ ...annotationsAsset,
+ ...simpleTestAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ class Foo {
+ dynamic a(int m, String n) => n + 1;
+ dynamic b(List<int> list) => list.length;
+ void c(String one, [String two, String three = ""]) => print('$one$two$three');
+ void d(String one, {String two, String three = ""}) => print('$one$two$three');
+ Future<void> e(String s) async => print(s);
+ // TODO(srawlins): Figure out async*; doesn't work yet. `isGenerator`
+ // does not appear to be working.
+ // Stream<void> f(String s) async* => print(s);
+ // Iterable<void> g(String s) sync* => print(s);
+ }
+ '''),
+ },
+ outputs: {
+ 'foo|test/foo_test.mocks.dart': dedent(r'''
+ /// A class which mocks [Foo].
+ ///
+ /// See the documentation for Mockito's code generation for more information.
+ class MockFoo extends Mock implements Foo {
+ dynamic a(int m, String n) =>
+ super.noSuchMethod(Invocation.method(#a, [m, n]));
+ dynamic b(List<int> list) =>
+ super.noSuchMethod(Invocation.method(#b, [list]));
+ void c(String one, [String two, String three = ""]) =>
+ super.noSuchMethod(Invocation.method(#c, [one, two, three]));
+ void d(String one, {String two, String three = ""}) => super
+ .noSuchMethod(Invocation.method(#d, [one], {#two: two, #three: three}));
+ Future<void> e(String s) async =>
+ super.noSuchMethod(Invocation.method(#e, [s]));
+ }
+ '''),
+ },
+ );
+ });
+
+ test('generates multiple mock classes', () async {
+ await testBuilder(
+ buildMocks(BuilderOptions({})),
+ {
+ ...annotationsAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ class Foo {
+ dynamic a(int m, String n) => n + 1;
+ }
+ class Bar {
+ dynamic b(List<int> list) => list.length;
+ }
+ '''),
+ 'foo|test/foo_test.dart': '''
+ import 'package:foo/foo.dart';
+ import 'package:mockito/annotations.dart';
+ @GenerateMocks([Foo, Bar])
+ void main() {}
+ '''
+ },
+ outputs: {
+ 'foo|test/foo_test.mocks.dart': dedent(r'''
+ /// A class which mocks [Foo].
+ ///
+ /// See the documentation for Mockito's code generation for more information.
+ class MockFoo extends Mock implements Foo {
+ dynamic a(int m, String n) =>
+ super.noSuchMethod(Invocation.method(#a, [m, n]));
+ }
+
+ /// A class which mocks [Bar].
+ ///
+ /// See the documentation for Mockito's code generation for more information.
+ class MockBar extends Mock implements Bar {
+ dynamic b(List<int> list) =>
+ super.noSuchMethod(Invocation.method(#b, [list]));
+ }
+ '''),
+ },
+ );
+ });
+
+ test('generates a mock class and overrides getters and setters', () async {
+ await testBuilder(
+ buildMocks(BuilderOptions({})),
+ {
+ ...annotationsAsset,
+ ...simpleTestAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ class Foo {
+ int get a => n + 1;
+ int _b;
+ set b(int value) => _b = value;
+ }
+ '''),
+ },
+ outputs: {
+ // TODO(srawlins): The getter will appear when it has a non-nullable
+ // return type.
+ 'foo|test/foo_test.mocks.dart': dedent(r'''
+ /// A class which mocks [Foo].
+ ///
+ /// See the documentation for Mockito's code generation for more information.
+ class MockFoo extends Mock implements Foo {
+ set b(int value) => super.noSuchMethod(Invocation.setter(#b, [value]));
+ }
+ '''),
+ },
+ );
+ });
+ test('throws given bad input', () async {
+ expect(
+ () async => await testBuilder(
+ buildMocks(BuilderOptions({})),
+ {
+ ...annotationsAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ class Foo {}
+ '''),
+ 'foo|test/foo_test.dart': dedent('''
+ // missing foo.dart import.
+ import 'package:mockito/annotations.dart';
+ @GenerateMocks([Foo])
+ void main() {}
+ '''),
+ },
+ ),
+ throwsStateError);
+ });
+}
+
+/// Dedent [input], so that each line is shifted to the left, so that the first
+/// line is at the 0 column.
+String dedent(String input) {
+ final indentMatch = RegExp(r'^(\s*)').firstMatch(input);
+ final indent = ''.padRight(indentMatch.group(1).length);
+ return input.splitMapJoin('\n',
+ onNonMatch: (s) => s.replaceFirst(RegExp('^$indent'), ''));
+}
diff --git a/test/capture_test.dart b/test/capture_test.dart
index 7c597ba..09da045 100644
--- a/test/capture_test.dart
+++ b/test/capture_test.dart
@@ -22,7 +22,7 @@
String methodWithNormalArgs(int x) => 'Real';
String methodWithListArgs(List<int> x) => 'Real';
String methodWithPositionalArgs(int x, [int y]) => 'Real';
- String methodWithTwoNamedArgs(int x, {int y, int z}) => 'Real';
+ String methodWithTwoNamedArgs(int x, {int y, int z}) => "Real";
set setter(String arg) {
throw StateError('I must be mocked');
}
diff --git a/test/deprecated_apis/mockito_test.dart b/test/deprecated_apis/mockito_test.dart
index ab9a4e3..95dbcb4 100644
--- a/test/deprecated_apis/mockito_test.dart
+++ b/test/deprecated_apis/mockito_test.dart
@@ -24,18 +24,18 @@
class _RealClass {
_RealClass innerObj;
- String methodWithoutArgs() => 'Real';
- String methodWithNormalArgs(int x) => 'Real';
- String methodWithListArgs(List<int> x) => 'Real';
- String methodWithPositionalArgs(int x, [int y]) => 'Real';
- String methodWithNamedArgs(int x, {int y}) => 'Real';
- String methodWithTwoNamedArgs(int x, {int y, int z}) => 'Real';
- String methodWithObjArgs(_RealClass x) => 'Real';
- Future<String> methodReturningFuture() => Future.value('Real');
- Stream<String> methodReturningStream() => Stream.fromIterable(['Real']);
- String get getter => 'Real';
+ String methodWithoutArgs() => "Real";
+ String methodWithNormalArgs(int x) => "Real";
+ String methodWithListArgs(List<int> x) => "Real";
+ String methodWithPositionalArgs(int x, [int y]) => "Real";
+ String methodWithNamedArgs(int x, {int y}) => "Real";
+ String methodWithTwoNamedArgs(int x, {int y, int z}) => "Real";
+ String methodWithObjArgs(_RealClass x) => "Real";
+ Future<String> methodReturningFuture() => Future.value("Real");
+ Stream<String> methodReturningStream() => Stream.fromIterable(["Real"]);
+ String get getter => "Real";
set setter(String arg) {
- throw StateError('I must be mocked');
+ throw StateError("I must be mocked");
}
}
@@ -54,23 +54,23 @@
class _MockedClass extends Mock implements _RealClass {}
-void expectFail(String expectedMessage, void Function() expectedToFail) {
+void expectFail(String expectedMessage, dynamic expectedToFail()) {
try {
expectedToFail();
- fail('It was expected to fail!');
+ fail("It was expected to fail!");
} catch (e) {
if (!(e is TestFailure)) {
rethrow;
} else {
if (expectedMessage != e.message) {
- throw TestFailure('Failed, but with wrong message: ${e.message}');
+ throw TestFailure("Failed, but with wrong message: ${e.message}");
}
}
}
}
-String noMatchingCallsFooter = '(If you called `verify(...).called(0);`, '
- 'please instead use `verifyNever(...);`.)';
+String noMatchingCallsFooter = "(If you called `verify(...).called(0);`, "
+ "please instead use `verifyNever(...);`.)";
void main() {
_MockedClass mock;
@@ -85,67 +85,67 @@
resetMockitoState();
});
- group('when()', () {
- test('should mock method with argument matcher', () {
+ group("when()", () {
+ test("should mock method with argument matcher", () {
when(mock.methodWithNormalArgs(typed(argThat(greaterThan(100)))))
- .thenReturn('A lot!');
+ .thenReturn("A lot!");
expect(mock.methodWithNormalArgs(100), isNull);
- expect(mock.methodWithNormalArgs(101), equals('A lot!'));
+ expect(mock.methodWithNormalArgs(101), equals("A lot!"));
});
- test('should mock method with any argument matcher', () {
- when(mock.methodWithNormalArgs(typed(any))).thenReturn('A lot!');
- expect(mock.methodWithNormalArgs(100), equals('A lot!'));
- expect(mock.methodWithNormalArgs(101), equals('A lot!'));
+ test("should mock method with any argument matcher", () {
+ when(mock.methodWithNormalArgs(typed(any))).thenReturn("A lot!");
+ expect(mock.methodWithNormalArgs(100), equals("A lot!"));
+ expect(mock.methodWithNormalArgs(101), equals("A lot!"));
});
- test('should mock method with any list argument matcher', () {
- when(mock.methodWithListArgs(typed(any))).thenReturn('A lot!');
- expect(mock.methodWithListArgs([42]), equals('A lot!'));
- expect(mock.methodWithListArgs([43]), equals('A lot!'));
+ test("should mock method with any list argument matcher", () {
+ when(mock.methodWithListArgs(typed(any))).thenReturn("A lot!");
+ expect(mock.methodWithListArgs([42]), equals("A lot!"));
+ expect(mock.methodWithListArgs([43]), equals("A lot!"));
});
- test('should mock method with mix of argument matchers and real things',
+ test("should mock method with mix of argument matchers and real things",
() {
when(mock.methodWithPositionalArgs(typed(argThat(greaterThan(100))), 17))
- .thenReturn('A lot with 17');
+ .thenReturn("A lot with 17");
expect(mock.methodWithPositionalArgs(100, 17), isNull);
expect(mock.methodWithPositionalArgs(101, 18), isNull);
- expect(mock.methodWithPositionalArgs(101, 17), equals('A lot with 17'));
+ expect(mock.methodWithPositionalArgs(101, 17), equals("A lot with 17"));
});
//no need to mock setter, except if we will have spies later...
- test('should mock method with thrown result', () {
+ test("should mock method with thrown result", () {
when(mock.methodWithNormalArgs(typed(any))).thenThrow(StateError('Boo'));
expect(() => mock.methodWithNormalArgs(42), throwsStateError);
});
- test('should mock method with calculated result', () {
+ test("should mock method with calculated result", () {
when(mock.methodWithNormalArgs(typed(any))).thenAnswer(
(Invocation inv) => inv.positionalArguments[0].toString());
- expect(mock.methodWithNormalArgs(43), equals('43'));
- expect(mock.methodWithNormalArgs(42), equals('42'));
+ expect(mock.methodWithNormalArgs(43), equals("43"));
+ expect(mock.methodWithNormalArgs(42), equals("42"));
});
- test('should mock method with calculated result', () {
+ test("should mock method with calculated result", () {
when(mock.methodWithNormalArgs(typed(argThat(equals(43)))))
- .thenReturn('43');
+ .thenReturn("43");
when(mock.methodWithNormalArgs(typed(argThat(equals(42)))))
- .thenReturn('42');
- expect(mock.methodWithNormalArgs(43), equals('43'));
+ .thenReturn("42");
+ expect(mock.methodWithNormalArgs(43), equals("43"));
});
- test('should mock hashCode', () {
+ test("should mock hashCode", () {
named(mock, hashCode: 42);
expect(mock.hashCode, equals(42));
});
- test('should have toString as name when it is not mocked', () {
- named(mock, name: 'Cat');
- expect(mock.toString(), equals('Cat'));
+ test("should have toString as name when it is not mocked", () {
+ named(mock, name: "Cat");
+ expect(mock.toString(), equals("Cat"));
});
- test('should mock equals between mocks when givenHashCode is equals', () {
+ test("should mock equals between mocks when givenHashCode is equals", () {
var anotherMock = named(_MockedClass(), hashCode: 42);
named(mock, hashCode: 42);
expect(mock == anotherMock, isTrue);
diff --git a/test/deprecated_apis/until_called_test.dart b/test/deprecated_apis/until_called_test.dart
index f4e6801..92ed487 100644
--- a/test/deprecated_apis/until_called_test.dart
+++ b/test/deprecated_apis/until_called_test.dart
@@ -86,7 +86,8 @@
});
group('untilCalled', () {
- var streamController = StreamController<CallMethodsEvent>.broadcast();
+ StreamController<CallMethodsEvent> streamController =
+ StreamController.broadcast();
group('on methods already called', () {
test('waits for method with normal args', () async {
diff --git a/test/deprecated_apis/verify_test.dart b/test/deprecated_apis/verify_test.dart
index 74cfdcb..a8c870c 100644
--- a/test/deprecated_apis/verify_test.dart
+++ b/test/deprecated_apis/verify_test.dart
@@ -57,7 +57,7 @@
class _MockedClass extends Mock implements _RealClass {}
-void expectFail(String expectedMessage, void Function() expectedToFail) {
+void expectFail(String expectedMessage, dynamic expectedToFail()) {
try {
expectedToFail();
fail('It was expected to fail!');
diff --git a/test/invocation_matcher_test.dart b/test/invocation_matcher_test.dart
index dc5bb52..12eae07 100644
--- a/test/invocation_matcher_test.dart
+++ b/test/invocation_matcher_test.dart
@@ -66,9 +66,9 @@
shouldFail(
call1,
isInvocation(call3),
- 'Expected: lie(<false>) '
+ "Expected: lie(<false>) "
"Actual: <Instance of '${call3.runtimeType}'> "
- 'Which: Does not match lie(<true>)',
+ "Which: Does not match lie(<true>)",
);
});
@@ -102,9 +102,9 @@
call1,
isInvocation(call3),
// RegExp needed because of https://github.com/dart-lang/sdk/issues/33565
- RegExp('Expected: set value=? <false> '
+ RegExp("Expected: set value=? <false> "
"Actual: <Instance of '${call3.runtimeType}'> "
- 'Which: Does not match set value=? <true>'),
+ "Which: Does not match set value=? <true>"),
);
});
});
@@ -118,7 +118,7 @@
shouldFail(
call,
invokes(#say, positionalArguments: [isNull]),
- 'Expected: say(null) '
+ "Expected: say(null) "
"Actual: <Instance of '${call.runtimeType}'> "
"Which: Does not match say('Hello')",
);
diff --git a/test/mockito_test.dart b/test/mockito_test.dart
index e31cde2..1b80150 100644
--- a/test/mockito_test.dart
+++ b/test/mockito_test.dart
@@ -21,16 +21,16 @@
class _RealClass {
_RealClass innerObj;
- String methodWithoutArgs() => 'Real';
- String methodWithNormalArgs(int x) => 'Real';
- String methodWithListArgs(List<int> x) => 'Real';
- String methodWithPositionalArgs(int x, [int y]) => 'Real';
- String methodWithNamedArgs(int x, {int y}) => 'Real';
- String methodWithTwoNamedArgs(int x, {int y, int z}) => 'Real';
- String methodWithObjArgs(_RealClass x) => 'Real';
- Future<String> methodReturningFuture() => Future.value('Real');
- Stream<String> methodReturningStream() => Stream.fromIterable(['Real']);
- String get getter => 'Real';
+ String methodWithoutArgs() => "Real";
+ String methodWithNormalArgs(int x) => "Real";
+ String methodWithListArgs(List<int> x) => "Real";
+ String methodWithPositionalArgs(int x, [int y]) => "Real";
+ String methodWithNamedArgs(int x, {int y}) => "Real";
+ String methodWithTwoNamedArgs(int x, {int y, int z}) => "Real";
+ String methodWithObjArgs(_RealClass x) => "Real";
+ Future<String> methodReturningFuture() => Future.value("Real");
+ Stream<String> methodReturningStream() => Stream.fromIterable(["Real"]);
+ String get getter => "Real";
}
abstract class _Foo {
@@ -43,30 +43,30 @@
String baz();
- String quux() => 'Real';
+ String quux() => "Real";
}
class _MockFoo extends _AbstractFoo with Mock {}
class _MockedClass extends Mock implements _RealClass {}
-void expectFail(String expectedMessage, void Function() expectedToFail) {
+void expectFail(String expectedMessage, dynamic expectedToFail()) {
try {
expectedToFail();
- fail('It was expected to fail!');
+ fail("It was expected to fail!");
} catch (e) {
if (!(e is TestFailure)) {
rethrow;
} else {
if (expectedMessage != e.message) {
- throw TestFailure('Failed, but with wrong message: ${e.message}');
+ throw TestFailure("Failed, but with wrong message: ${e.message}");
}
}
}
}
-String noMatchingCallsFooter = '(If you called `verify(...).called(0);`, '
- 'please instead use `verifyNever(...);`.)';
+String noMatchingCallsFooter = "(If you called `verify(...).called(0);`, "
+ "please instead use `verifyNever(...);`.)";
void main() {
_MockedClass mock;
@@ -83,229 +83,229 @@
resetMockitoState();
});
- group('mixin support', () {
- test('should work', () {
+ group("mixin support", () {
+ test("should work", () {
var foo = _MockFoo();
when(foo.baz()).thenReturn('baz');
expect(foo.bar(), 'baz');
});
});
- group('when()', () {
- test('should mock method without args', () {
- when(mock.methodWithoutArgs()).thenReturn('A');
- expect(mock.methodWithoutArgs(), equals('A'));
+ group("when()", () {
+ test("should mock method without args", () {
+ when(mock.methodWithoutArgs()).thenReturn("A");
+ expect(mock.methodWithoutArgs(), equals("A"));
});
- test('should mock method with normal args', () {
- when(mock.methodWithNormalArgs(42)).thenReturn('Ultimate Answer');
+ test("should mock method with normal args", () {
+ when(mock.methodWithNormalArgs(42)).thenReturn("Ultimate Answer");
expect(mock.methodWithNormalArgs(43), isNull);
- expect(mock.methodWithNormalArgs(42), equals('Ultimate Answer'));
+ expect(mock.methodWithNormalArgs(42), equals("Ultimate Answer"));
});
- test('should mock method with mock args', () {
+ test("should mock method with mock args", () {
var m1 = _MockedClass();
- when(mock.methodWithObjArgs(m1)).thenReturn('Ultimate Answer');
+ when(mock.methodWithObjArgs(m1)).thenReturn("Ultimate Answer");
expect(mock.methodWithObjArgs(_MockedClass()), isNull);
- expect(mock.methodWithObjArgs(m1), equals('Ultimate Answer'));
+ expect(mock.methodWithObjArgs(m1), equals("Ultimate Answer"));
});
- test('should mock method with positional args', () {
- when(mock.methodWithPositionalArgs(42, 17)).thenReturn('Answer and...');
+ test("should mock method with positional args", () {
+ when(mock.methodWithPositionalArgs(42, 17)).thenReturn("Answer and...");
expect(mock.methodWithPositionalArgs(42), isNull);
expect(mock.methodWithPositionalArgs(42, 18), isNull);
- expect(mock.methodWithPositionalArgs(42, 17), equals('Answer and...'));
+ expect(mock.methodWithPositionalArgs(42, 17), equals("Answer and..."));
});
- test('should mock method with named args', () {
- when(mock.methodWithNamedArgs(42, y: 17)).thenReturn('Why answer?');
+ test("should mock method with named args", () {
+ when(mock.methodWithNamedArgs(42, y: 17)).thenReturn("Why answer?");
expect(mock.methodWithNamedArgs(42), isNull);
expect(mock.methodWithNamedArgs(42, y: 18), isNull);
- expect(mock.methodWithNamedArgs(42, y: 17), equals('Why answer?'));
+ expect(mock.methodWithNamedArgs(42, y: 17), equals("Why answer?"));
});
- test('should mock method with List args', () {
- when(mock.methodWithListArgs([42])).thenReturn('Ultimate answer');
+ test("should mock method with List args", () {
+ when(mock.methodWithListArgs([42])).thenReturn("Ultimate answer");
expect(mock.methodWithListArgs([43]), isNull);
- expect(mock.methodWithListArgs([42]), equals('Ultimate answer'));
+ expect(mock.methodWithListArgs([42]), equals("Ultimate answer"));
});
- test('should mock method with argument matcher', () {
+ test("should mock method with argument matcher", () {
when(mock.methodWithNormalArgs(argThat(greaterThan(100))))
- .thenReturn('A lot!');
+ .thenReturn("A lot!");
expect(mock.methodWithNormalArgs(100), isNull);
- expect(mock.methodWithNormalArgs(101), equals('A lot!'));
+ expect(mock.methodWithNormalArgs(101), equals("A lot!"));
});
- test('should mock method with any argument matcher', () {
- when(mock.methodWithNormalArgs(any)).thenReturn('A lot!');
- expect(mock.methodWithNormalArgs(100), equals('A lot!'));
- expect(mock.methodWithNormalArgs(101), equals('A lot!'));
+ test("should mock method with any argument matcher", () {
+ when(mock.methodWithNormalArgs(any)).thenReturn("A lot!");
+ expect(mock.methodWithNormalArgs(100), equals("A lot!"));
+ expect(mock.methodWithNormalArgs(101), equals("A lot!"));
});
- test('should mock method with any list argument matcher', () {
- when(mock.methodWithListArgs(any)).thenReturn('A lot!');
- expect(mock.methodWithListArgs([42]), equals('A lot!'));
- expect(mock.methodWithListArgs([43]), equals('A lot!'));
+ test("should mock method with any list argument matcher", () {
+ when(mock.methodWithListArgs(any)).thenReturn("A lot!");
+ expect(mock.methodWithListArgs([42]), equals("A lot!"));
+ expect(mock.methodWithListArgs([43]), equals("A lot!"));
});
- test('should mock method with multiple named args and matchers', () {
+ test("should mock method with multiple named args and matchers", () {
when(mock.methodWithTwoNamedArgs(any, y: anyNamed('y')))
- .thenReturn('x y');
+ .thenReturn("x y");
when(mock.methodWithTwoNamedArgs(any, z: anyNamed('z')))
- .thenReturn('x z');
+ .thenReturn("x z");
if (isNsmForwarding) {
- expect(mock.methodWithTwoNamedArgs(42), 'x z');
+ expect(mock.methodWithTwoNamedArgs(42), "x z");
} else {
expect(mock.methodWithTwoNamedArgs(42), isNull);
}
- expect(mock.methodWithTwoNamedArgs(42, y: 18), equals('x y'));
- expect(mock.methodWithTwoNamedArgs(42, z: 17), equals('x z'));
+ expect(mock.methodWithTwoNamedArgs(42, y: 18), equals("x y"));
+ expect(mock.methodWithTwoNamedArgs(42, z: 17), equals("x z"));
expect(mock.methodWithTwoNamedArgs(42, y: 18, z: 17), isNull);
when(mock.methodWithTwoNamedArgs(any, y: anyNamed('y'), z: anyNamed('z')))
- .thenReturn('x y z');
- expect(mock.methodWithTwoNamedArgs(42, y: 18, z: 17), equals('x y z'));
+ .thenReturn("x y z");
+ expect(mock.methodWithTwoNamedArgs(42, y: 18, z: 17), equals("x y z"));
});
- test('should mock method with mix of argument matchers and real things',
+ test("should mock method with mix of argument matchers and real things",
() {
when(mock.methodWithPositionalArgs(argThat(greaterThan(100)), 17))
- .thenReturn('A lot with 17');
+ .thenReturn("A lot with 17");
expect(mock.methodWithPositionalArgs(100, 17), isNull);
expect(mock.methodWithPositionalArgs(101, 18), isNull);
- expect(mock.methodWithPositionalArgs(101, 17), equals('A lot with 17'));
+ expect(mock.methodWithPositionalArgs(101, 17), equals("A lot with 17"));
});
- test('should mock getter', () {
- when(mock.getter).thenReturn('A');
- expect(mock.getter, equals('A'));
+ test("should mock getter", () {
+ when(mock.getter).thenReturn("A");
+ expect(mock.getter, equals("A"));
});
- test('should have hashCode when it is not mocked', () {
+ test("should have hashCode when it is not mocked", () {
expect(mock.hashCode, isNotNull);
});
- test('should have default toString when it is not mocked', () {
+ test("should have default toString when it is not mocked", () {
expect(mock.toString(), equals('_MockedClass'));
});
- test('should use identical equality between it is not mocked', () {
+ test("should use identical equality between it is not mocked", () {
var anotherMock = _MockedClass();
expect(mock == anotherMock, isFalse);
expect(mock == mock, isTrue);
});
- test('should mock method with thrown result', () {
+ test("should mock method with thrown result", () {
when(mock.methodWithNormalArgs(any)).thenThrow(StateError('Boo'));
expect(() => mock.methodWithNormalArgs(42), throwsStateError);
});
- test('should mock method with calculated result', () {
+ test("should mock method with calculated result", () {
when(mock.methodWithNormalArgs(any)).thenAnswer(
(Invocation inv) => inv.positionalArguments[0].toString());
- expect(mock.methodWithNormalArgs(43), equals('43'));
- expect(mock.methodWithNormalArgs(42), equals('42'));
+ expect(mock.methodWithNormalArgs(43), equals("43"));
+ expect(mock.methodWithNormalArgs(42), equals("42"));
});
- test('should return mock to make simple oneline mocks', () {
+ test("should return mock to make simple oneline mocks", () {
_RealClass mockWithSetup = _MockedClass();
- when(mockWithSetup.methodWithoutArgs()).thenReturn('oneline');
- expect(mockWithSetup.methodWithoutArgs(), equals('oneline'));
+ when(mockWithSetup.methodWithoutArgs()).thenReturn("oneline");
+ expect(mockWithSetup.methodWithoutArgs(), equals("oneline"));
});
- test('should use latest matching when definition', () {
- when(mock.methodWithoutArgs()).thenReturn('A');
- when(mock.methodWithoutArgs()).thenReturn('B');
- expect(mock.methodWithoutArgs(), equals('B'));
+ test("should use latest matching when definition", () {
+ when(mock.methodWithoutArgs()).thenReturn("A");
+ when(mock.methodWithoutArgs()).thenReturn("B");
+ expect(mock.methodWithoutArgs(), equals("B"));
});
- test('should mock method with calculated result', () {
- when(mock.methodWithNormalArgs(argThat(equals(43)))).thenReturn('43');
- when(mock.methodWithNormalArgs(argThat(equals(42)))).thenReturn('42');
- expect(mock.methodWithNormalArgs(43), equals('43'));
+ test("should mock method with calculated result", () {
+ when(mock.methodWithNormalArgs(argThat(equals(43)))).thenReturn("43");
+ when(mock.methodWithNormalArgs(argThat(equals(42)))).thenReturn("42");
+ expect(mock.methodWithNormalArgs(43), equals("43"));
});
// Error path tests.
- test('should throw if `when` is called while stubbing', () {
+ test("should throw if `when` is called while stubbing", () {
expect(() {
var responseHelper = () {
var mock2 = _MockedClass();
- when(mock2.getter).thenReturn('A');
+ when(mock2.getter).thenReturn("A");
return mock2;
};
when(mock.innerObj).thenReturn(responseHelper());
}, throwsStateError);
});
- test('thenReturn throws if provided Future', () {
+ test("thenReturn throws if provided Future", () {
expect(
() => when(mock.methodReturningFuture())
- .thenReturn(Future.value('stub')),
+ .thenReturn(Future.value("stub")),
throwsArgumentError);
});
- test('thenReturn throws if provided Stream', () {
+ test("thenReturn throws if provided Stream", () {
expect(
() => when(mock.methodReturningStream())
- .thenReturn(Stream.fromIterable(['stub'])),
+ .thenReturn(Stream.fromIterable(["stub"])),
throwsArgumentError);
});
- test('thenAnswer supports stubbing method returning a Future', () async {
+ test("thenAnswer supports stubbing method returning a Future", () async {
when(mock.methodReturningFuture())
- .thenAnswer((_) => Future.value('stub'));
+ .thenAnswer((_) => Future.value("stub"));
- expect(await mock.methodReturningFuture(), 'stub');
+ expect(await mock.methodReturningFuture(), "stub");
});
- test('thenAnswer supports stubbing method returning a Stream', () async {
+ test("thenAnswer supports stubbing method returning a Stream", () async {
when(mock.methodReturningStream())
- .thenAnswer((_) => Stream.fromIterable(['stub']));
+ .thenAnswer((_) => Stream.fromIterable(["stub"]));
- expect(await mock.methodReturningStream().toList(), ['stub']);
+ expect(await mock.methodReturningStream().toList(), ["stub"]);
});
- test('should throw if named matcher is passed as the wrong name', () {
+ test("should throw if named matcher is passed as the wrong name", () {
expect(() {
- when(mock.methodWithNamedArgs(argThat(equals(42)), y: anyNamed('z')))
- .thenReturn('99');
+ when(mock.methodWithNamedArgs(argThat(equals(42)), y: anyNamed("z")))
+ .thenReturn("99");
}, throwsArgumentError);
});
- test('should throw if attempting to stub a real method', () {
+ test("should throw if attempting to stub a real method", () {
var foo = _MockFoo();
expect(() {
- when(foo.quux()).thenReturn('Stub');
+ when(foo.quux()).thenReturn("Stub");
}, throwsStateError);
});
});
- group('throwOnMissingStub', () {
- test('should throw when a mock was called without a matching stub', () {
+ group("throwOnMissingStub", () {
+ test("should throw when a mock was called without a matching stub", () {
throwOnMissingStub(mock);
- when(mock.methodWithNormalArgs(42)).thenReturn('Ultimate Answer');
+ when(mock.methodWithNormalArgs(42)).thenReturn("Ultimate Answer");
expect(
() => (mock).methodWithoutArgs(),
throwsNoSuchMethodError,
);
});
- test('should not throw when a mock was called with a matching stub', () {
+ test("should not throw when a mock was called with a matching stub", () {
throwOnMissingStub(mock);
- when(mock.methodWithoutArgs()).thenReturn('A');
+ when(mock.methodWithoutArgs()).thenReturn("A");
expect(() => mock.methodWithoutArgs(), returnsNormally);
});
});
test(
- 'reports an error when using an argument matcher outside of stubbing or '
- 'verification', () {
+ "reports an error when using an argument matcher outside of stubbing or "
+ "verification", () {
expect(() => mock.methodWithNormalArgs(any), throwsArgumentError);
});
test(
- 'reports an error when using an argument matcher in a position other '
- 'than an argument for the stubbed method', () {
+ "reports an error when using an argument matcher in a position other "
+ "than an argument for the stubbed method", () {
expect(() => when(mock.methodWithListArgs(List.filled(7, any))),
throwsArgumentError);
});
diff --git a/test/until_called_test.dart b/test/until_called_test.dart
index 4a0b03f..634c59c 100644
--- a/test/until_called_test.dart
+++ b/test/until_called_test.dart
@@ -81,7 +81,8 @@
});
group('untilCalled', () {
- var streamController = StreamController<CallMethodsEvent>.broadcast();
+ StreamController<CallMethodsEvent> streamController =
+ StreamController.broadcast();
group('on methods already called', () {
test('waits for method without args', () async {
diff --git a/test/verify_test.dart b/test/verify_test.dart
index 7622a2b..0c41c29 100644
--- a/test/verify_test.dart
+++ b/test/verify_test.dart
@@ -54,7 +54,7 @@
class _MockedClass extends Mock implements _RealClass {}
-void expectFail(Pattern expectedMessage, void Function() expectedToFail) {
+void expectFail(Pattern expectedMessage, dynamic expectedToFail()) {
try {
expectedToFail();
fail('It was expected to fail!');