Mockito codegen: use fallbackGenerator when present to create a default 'returnValueForMissingStub'.
When running in sound mode, this allows overriding the default missingStub implementation that might fail at runtime (generics for ex.).
PiperOrigin-RevId: 472925798
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
index a70d830..2935c43 100644
--- a/lib/src/builder.dart
+++ b/lib/src/builder.dart
@@ -1350,9 +1350,15 @@
returnValueForMissingStub =
_futureReference(refer('void')).property('value').call([]);
} else if (mockTarget.onMissingStub == OnMissingStub.returnDefault) {
- // Return a legal default value if no stub is found which matches a real
- // call.
- returnValueForMissingStub = _dummyValue(returnType, invocation);
+ if (fallbackGenerator != null) {
+ // Re-use the fallback for missing stub.
+ returnValueForMissingStub =
+ _fallbackGeneratorCode(method, fallbackGenerator);
+ } else {
+ // Return a legal default value if no stub is found which matches a real
+ // call.
+ returnValueForMissingStub = _dummyValue(returnType, invocation);
+ }
}
final namedArgs = {
if (fallbackGenerator != null)
@@ -1819,7 +1825,9 @@
else if (typeSystem._returnTypeIsNonNullable(getter))
'returnValue': _dummyValue(returnType, invocation),
if (mockTarget.onMissingStub == OnMissingStub.returnDefault)
- 'returnValueForMissingStub': _dummyValue(returnType, invocation),
+ 'returnValueForMissingStub': (fallbackGenerator != null
+ ? _fallbackGeneratorCode(getter, fallbackGenerator)
+ : _dummyValue(returnType, invocation)),
};
var superNoSuchMethod =
refer('super').property('noSuchMethod').call([invocation], namedArgs);
diff --git a/test/builder/custom_mocks_test.dart b/test/builder/custom_mocks_test.dart
index 0a5ab48..3d653a3 100644
--- a/test/builder/custom_mocks_test.dart
+++ b/test/builder/custom_mocks_test.dart
@@ -936,6 +936,39 @@
});
test(
+ 'generates mock classes including a fallback generator and '
+ 'OnMissingStub.returnDefault', () async {
+ var mocksContent = await buildWithNonNullable({
+ ...annotationsAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ abstract class Foo<T> {
+ T get f;
+ }
+ '''),
+ 'foo|test/foo_test.dart': '''
+ import 'package:foo/foo.dart';
+ import 'package:mockito/annotations.dart';
+
+ T fShim<T>() {
+ throw 'unknown';
+ }
+
+ @GenerateMocks(
+ [],
+ customMocks: [
+ MockSpec<Foo>(
+ fallbackGenerators: {#f: fShim},
+ onMissingStub: OnMissingStub.returnDefault),
+ ],
+ )
+ void main() {}
+ '''
+ });
+ expect(mocksContent, contains('returnValue: _i3.fShim(),'));
+ expect(mocksContent, contains('returnValueForMissingStub: _i3.fShim(),'));
+ });
+
+ test(
'throws when GenerateMocks is given a class with a type parameter with a '
'private bound', () async {
_expectBuilderThrows(
@@ -1265,7 +1298,36 @@
'''
});
expect(mocksContent, isNot(contains('throwOnMissingStub')));
- expect(mocksContent, contains('returnValueForMissingStub:'));
+ expect(mocksContent, contains('returnValue: 0'));
+ expect(mocksContent, contains('returnValueForMissingStub: 0'));
+ });
+
+ test(
+ 'generates a mock class which uses the new behavior of returning '
+ 'a valid value for missing stubs, if GenerateNiceMocks and '
+ 'fallbackGenerators were used', () async {
+ var mocksContent = await buildWithNonNullable({
+ ...annotationsAsset,
+ 'foo|lib/foo.dart': dedent(r'''
+ class Foo<T> {
+ int m();
+ }
+ '''),
+ 'foo|test/foo_test.dart': '''
+ import 'package:foo/foo.dart';
+ import 'package:mockito/annotations.dart';
+
+ int mShim() {
+ return 1;
+ }
+
+ @GenerateNiceMocks([MockSpec<Foo>(fallbackGenerators: {#m: mShim})])
+ void main() {}
+ '''
+ });
+ expect(mocksContent, isNot(contains('throwOnMissingStub')));
+ expect(mocksContent, contains('returnValue: _i3.mShim(),'));
+ expect(mocksContent, contains('returnValueForMissingStub: _i3.mShim(),'));
});
test('mixed GenerateMocks and GenerateNiceMocks annotations could be used',