MockBuilder: Override getters; correct bugs in overriding setters.
Getter support is added here, and only public non-nullable instance getters are overridden.
Setter support is improved; previously all public instance setters were overridden. This change ensures that only non-nullable setters are overridden.
Small tests are added for fields, final fields, getters, and setters.
PiperOrigin-RevId: 315014983
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
index 99a2a3f..c637462 100644
--- a/lib/src/builder.dart
+++ b/lib/src/builder.dart
@@ -176,9 +176,13 @@
if (field.isPrivate || field.isStatic) {
continue;
}
- // Handle getters when we handle non-nullable return types.
+ final getter = field.getter;
+ if (getter != null && _returnTypeIsNonNullable(getter)) {
+ cBuilder.methods.add(
+ Method((mBuilder) => _buildOverridingGetter(mBuilder, getter)));
+ }
final setter = field.setter;
- if (setter != null) {
+ if (setter != null && _hasNonNullableParameter(setter)) {
cBuilder.methods.add(
Method((mBuilder) => _buildOverridingSetter(mBuilder, setter)));
}
@@ -196,7 +200,7 @@
});
}
- bool _returnTypeIsNonNullable(MethodElement method) =>
+ bool _returnTypeIsNonNullable(ExecutableElement method) =>
typeSystem.isPotentiallyNonNullable(method.returnType);
// Returns whether [method] has at least one parameter whose type is
@@ -210,7 +214,7 @@
// }
// final c1 = C<int?>(); // m's parameter's type is nullable.
// final c2 = C<int>(); // m's parameter's type is non-nullable.
- bool _hasNonNullableParameter(MethodElement method) =>
+ bool _hasNonNullableParameter(ExecutableElement method) =>
method.parameters.any((p) => typeSystem.isPotentiallyNonNullable(p.type));
/// Build a method which overrides [method], with all non-nullable
@@ -222,8 +226,6 @@
// tests for typedefs, old-style function parameters, function types, type
// variables, non-nullable type variables (bounded to Object, I think),
// dynamic.
- // 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*.
var name = method.displayName;
@@ -261,10 +263,7 @@
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`.
+
final invocation = refer('Invocation').property('method').call([
refer('#${method.displayName}'),
literalList(invocationPositionalArgs),
@@ -392,6 +391,27 @@
});
}
+ /// Build a getter which overrides [getter].
+ ///
+ /// This new method just calls `super.noSuchMethod`, optionally passing a
+ /// return value for non-nullable getters.
+ void _buildOverridingGetter(
+ MethodBuilder builder, PropertyAccessorElement getter) {
+ builder
+ ..name = getter.displayName
+ ..type = MethodType.getter
+ ..returns = _typeReference(getter.returnType);
+
+ final invocation = refer('Invocation').property('getter').call([
+ refer('#${getter.displayName}'),
+ ]);
+ final noSuchMethodArgs = [invocation, _dummyValue(getter.returnType)];
+ final returnNoSuchMethod =
+ refer('super').property('noSuchMethod').call(noSuchMethodArgs);
+
+ builder.body = returnNoSuchMethod.code;
+ }
+
/// Build a setter which overrides [setter], widening the single parameter
/// type to be nullable if it is non-nullable.
///
diff --git a/test/builder_test.dart b/test/builder_test.dart
index 37c6188..fb550e8 100644
--- a/test/builder_test.dart
+++ b/test/builder_test.dart
@@ -73,34 +73,6 @@
});
test(
- 'generates mock for an imported class but does not override private '
- 'or static fields', () async {
- await _testWithNonNullable(
- {
- ...annotationsAsset,
- ...simpleTestAsset,
- 'foo|lib/foo.dart': dedent(r'''
- class Foo {
- int _a;
- static int b;
- }
- '''),
- },
- outputs: {
- 'foo|test/foo_test.mocks.dart': dedent(r'''
- import 'package:mockito/mockito.dart' as _i1;
- import 'package:foo/foo.dart' as _i2;
-
- /// 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(
'generates mock for an imported class but does not override any '
'extension methods', () async {
await _testWithNonNullable(
@@ -664,34 +636,108 @@
);
});
- test('overrides getters and setters', () async {
- await _testWithNonNullable(
- {
- ...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'''
- import 'package:mockito/mockito.dart' as _i1;
- import 'package:foo/foo.dart' as _i2;
+ test('overrides non-nullable instance getters', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ int get m => 7;
+ }
+ '''),
+ _containsAllOf(
+ 'int get m => super.noSuchMethod(Invocation.getter(#m), 0);'),
+ );
+ });
- /// A class which mocks [Foo].
- ///
- /// See the documentation for Mockito's code generation for more information.
- class MockFoo extends _i1.Mock implements _i2.Foo {
- set b(int value) => super.noSuchMethod(Invocation.setter(#b, [value]));
- }
- '''),
- },
+ test('does not override nullable instance getters', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ int? get m => 7;
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+ );
+ });
+
+ test('overrides non-nullable instance setters', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ void set m(int a) {}
+ }
+ '''),
+ _containsAllOf(
+ 'set m(int a) => super.noSuchMethod(Invocation.setter(#m, [a]));'),
+ );
+ });
+
+ test('does not override nullable instance setters', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ void set m(int? a) {}
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+ );
+ });
+
+ test('overrides non-nullable fields', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ int m;
+ }
+ '''),
+ _containsAllOf(
+ 'int get m => super.noSuchMethod(Invocation.getter(#m), 0);',
+ 'set m(int _m) => super.noSuchMethod(Invocation.setter(#m, [_m]));'),
+ );
+ });
+
+ test('overrides final non-nullable fields', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ final int m;
+ Foo(this.m);
+ }
+ '''),
+ _containsAllOf(
+ 'int get m => super.noSuchMethod(Invocation.getter(#m), 0);'),
+ );
+ });
+
+ test('does not override nullable fields', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ int? m;
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+ );
+ });
+
+ test('does not override private fields', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ int _a;
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
+ );
+ });
+
+ test('does not override static fields', () async {
+ await _expectSingleNonNullableOutput(
+ dedent(r'''
+ class Foo {
+ static int b;
+ }
+ '''),
+ _containsAllOf('class MockFoo extends _i1.Mock implements _i2.Foo {}'),
);
});