MockBuilder: Do not be concerned with method modifiers.
An overriding method and an overridden method need not have matching modifiers. For example, an async method can be overridden by a synchronous method; the modifiers are not part of the signature.
Introduce support for returning a simple dummy Iterable (an empty List) and a simple dummy Stream (an empty Stream).
Improve existing tests and introduce new tests.
PiperOrigin-RevId: 315357440
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
index a543527..e1fe972 100644
--- a/lib/src/builder.dart
+++ b/lib/src/builder.dart
@@ -15,6 +15,7 @@
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart' as analyzer;
+import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/dart/element/type_system.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart';
@@ -62,6 +63,7 @@
final mockLibrary = Library((b) {
var mockLibraryInfo = _MockLibraryInfo(classesToMock,
sourceLibIsNonNullable: sourceLibIsNonNullable,
+ typeProvider: entryLib.typeProvider,
typeSystem: entryLib.typeSystem);
b.body.addAll(mockLibraryInfo.fakeClasses);
b.body.addAll(mockLibraryInfo.mockClasses);
@@ -89,6 +91,9 @@
class _MockLibraryInfo {
final bool sourceLibIsNonNullable;
+ /// The type provider which applies to the source library.
+ final TypeProvider typeProvider;
+
/// The type system which applies to the source library.
final TypeSystem typeSystem;
@@ -108,7 +113,7 @@
/// Build mock classes for [classesToMock], a list of classes obtained from a
/// `@GenerateMocks` annotation.
_MockLibraryInfo(List<DartObject> classesToMock,
- {this.sourceLibIsNonNullable, this.typeSystem}) {
+ {this.sourceLibIsNonNullable, this.typeProvider, this.typeSystem}) {
for (final classToMock in classesToMock) {
final dartTypeToMock = classToMock.toTypeValue();
if (dartTypeToMock == null) {
@@ -225,7 +230,6 @@
// TODO(srawlins): Include widening tests for typedefs, old-style function
// parameters, function types.
void _buildOverridingMethod(MethodBuilder builder, MethodElement method) {
- // TODO(srawlins): generator methods like async*, sync*.
var name = method.displayName;
if (method.isOperator) name = 'operator$name';
builder
@@ -236,13 +240,6 @@
builder.types.addAll(method.typeParameters.map(_typeParameterReference));
}
- 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>[];
@@ -293,6 +290,8 @@
.call([_dummyValue(typeArgument)]);
} else if (type.isDartCoreInt) {
return literalNum(0);
+ } else if (type.isDartCoreIterable) {
+ return literalList([]);
} else if (type.isDartCoreList) {
return literalList([]);
} else if (type.isDartCoreMap) {
@@ -303,6 +302,8 @@
// This is perhaps a dangerous hack. The code, `{}`, is parsed as a Set
// literal if it is used in a context which explicitly expects a Set.
return literalMap({});
+ } else if (type.element?.declaration == typeProvider.streamElement) {
+ return refer('Stream').property('empty').call([]);
} else if (type.isDartCoreString) {
return literalString('');
} else {
diff --git a/test/builder_test.dart b/test/builder_test.dart
index 11273ee..fafa3a1 100644
--- a/test/builder_test.dart
+++ b/test/builder_test.dart
@@ -100,48 +100,63 @@
);
});
- test('generates a mock class and overrides method parameters', () async {
- await _testWithNonNullable(
- {
- ...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'''
- import 'package:mockito/mockito.dart' as _i1;
- import 'package:foo/foo.dart' as _i2;
- import 'dart:async' as _i3;
+ test('overrides methods, matching optional positional parameters', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ void m(int a, [int b, int c = 0]) {}
+ }
+ '''),
+ _containsAllOf('void m(int? a, [int? b, int? c = 0]) =>',
+ 'super.noSuchMethod(Invocation.method(#m, [a, b, c]));'),
+ );
+ });
- /// A class which mocks [Foo].
- ///
- /// See the documentation for Mockito's code generation for more information.
- class MockFoo extends _i1.Mock implements _i2.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}));
- _i3.Future<void> e(String? s) async =>
- super.noSuchMethod(Invocation.method(#e, [s]), Future.value(null));
- }
- '''),
- },
+ test('overrides methods, matching named parameters', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ void m(int a, {int b, int c = 0}) {}
+ }
+ '''),
+ _containsAllOf('void m(int? a, {int? b, int? c = 0}) =>',
+ 'super.noSuchMethod(Invocation.method(#m, [a], {#b: b, #c: c}));'),
+ );
+ });
+
+ test('overrides async methods legally', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ Future<void> m() async => print(s);
+ }
+ '''),
+ _containsAllOf('_i3.Future<void> m() =>',
+ 'super.noSuchMethod(Invocation.method(#m, []), Future.value(null));'),
+ );
+ });
+
+ test('overrides async* methods legally', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ Stream<int> m() async* { yield 7; }
+ }
+ '''),
+ _containsAllOf('_i3.Stream<int> m() =>',
+ 'super.noSuchMethod(Invocation.method(#m, []), Stream.empty());'),
+ );
+ });
+
+ test('overrides sync* methods legally', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ Iterable<int> m() sync* { yield 7; }
+ }
+ '''),
+ _containsAllOf('Iterable<int> m() =>',
+ 'super.noSuchMethod(Invocation.method(#m, []), []);'),
);
});
@@ -558,32 +573,61 @@
);
});
- test('does not override methods with a nullable return type', () async {
- await _testWithNonNullable(
- {
- ...annotationsAsset,
- ...simpleTestAsset,
- 'foo|lib/foo.dart': dedent(r'''
- abstract class Foo {
- void a();
- b();
- dynamic c();
- void d();
- int? f();
- }
- '''),
- },
- outputs: {
- 'foo|test/foo_test.mocks.dart': dedent(r'''
- import 'package:mockito/mockito.dart' as _i1;
- import 'package:foo/foo.dart' as _i2;
+ test('does not override methods with a void return type', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ abstract class Foo {
+ void m();
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+ );
+ });
- /// A class which mocks [Foo].
- ///
- /// See the documentation for Mockito's code generation for more information.
- class MockFoo extends _i1.Mock implements _i2.Foo {}
- '''),
- },
+ test('does not override methods with an implicit dynamic return type',
+ () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ abstract class Foo {
+ m();
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+ );
+ });
+
+ test('does not override methods with an explicit dynamic return type',
+ () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ abstract class Foo {
+ dynamic m();
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+ );
+ });
+
+ test('does not override methods with a nullable return type', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ abstract class Foo {
+ int? m();
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+ );
+ });
+
+ test('overrides methods with a non-nullable return type', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ abstract class Foo {
+ int m();
+ }
+ '''),
+ _containsAllOf(
+ 'int m() => super.noSuchMethod(Invocation.method(#m, []), 0);'),
);
});
@@ -876,11 +920,11 @@
await _expectSingleNonNullableOutput(
dedent(r'''
class Foo {
- Future<bool> m1() async => false;
+ Future<bool> m() async => false;
}
'''),
- _containsAllOf('_i3.Future<bool> m1() async =>',
- 'super.noSuchMethod(Invocation.method(#m1, []), Future.value(false));'),
+ _containsAllOf('_i3.Future<bool> m() =>',
+ 'super.noSuchMethod(Invocation.method(#m, []), Future.value(false));'),
);
});