diff --git a/.gitignore b/.gitignore index 7621c584..b3bfe992 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,9 @@ .packages .pub pubspec.lock +build/ coverage/ + +# ignore generated mocks +**/*.mocks.dart \ No newline at end of file diff --git a/lib/src/builder.dart b/lib/src/builder.dart index 2a6989d2..b950046b 100644 --- a/lib/src/builder.dart +++ b/lib/src/builder.dart @@ -1233,8 +1233,9 @@ class _MockClassInfo { if (parameter.isNamed) pBuilder.named = true; if (parameter.defaultValueCode != null) { try { - pBuilder.defaultTo = - _expressionFromDartObject(parameter.computeConstantValue()!).code; + pBuilder.defaultTo = _expressionFromDartObject( + parameter.computeConstantValue()!, parameter) + .code; } on _ReviveException catch (e) { final method = parameter.enclosingElement!; final clazz = method.enclosingElement!; @@ -1248,11 +1249,13 @@ class _MockClassInfo { } /// Creates a code_builder [Expression] from [object], a constant object from - /// analyzer. + /// analyzer and [parameter], an optional [ParameterElement], when the + /// expression is created for a method parameter. /// /// This is very similar to Angular's revive code, in /// angular_compiler/analyzer/di/injector.dart. - Expression _expressionFromDartObject(DartObject object) { + Expression _expressionFromDartObject(DartObject object, + [ParameterElement? parameter]) { final constant = ConstantReader(object); if (constant.isNull) { return literalNull; @@ -1318,7 +1321,10 @@ class _MockClassInfo { for (var pair in revivable.namedArguments.entries) pair.key: _expressionFromDartObject(pair.value) }; - final type = referImported(name, _typeImport(object.type!.element)); + final element = parameter != null && name != object.type!.element!.name + ? parameter.type.element + : object.type!.element; + final type = referImported(name, _typeImport(element)); if (revivable.accessor.isNotEmpty) { return type.constInstanceNamed( revivable.accessor, diff --git a/test/end2end/foo.dart b/test/end2end/foo.dart index fa6e6a4d..1ba22ff0 100644 --- a/test/end2end/foo.dart +++ b/test/end2end/foo.dart @@ -1,9 +1,18 @@ +import 'foo_sub.dart'; + class Foo { + const Foo(); + + const factory Foo.sub() = FooSub2; + String positionalParameter(int x) => 'Real'; String namedParameter({required int x}) => 'Real'; String get getter => 'Real'; int operator +(int arg) => arg + 1; String parameterWithDefault([int x = 0]) => 'Real'; + String parameterWithDefault2([Foo x = const FooSub()]) => 'Real'; + String parameterWithDefaultFactoryRedirect([Foo x = const Foo.sub()]) => + 'Real'; String? nullableMethod(int x) => 'Real'; String? get nullableGetter => 'Real'; String methodWithBarArg(Bar bar) => 'result'; @@ -13,8 +22,6 @@ class Foo { Future? returnsNullableFutureVoid() => Future.value(); } -class FooSub extends Foo {} - class Bar {} abstract class Baz { diff --git a/test/end2end/foo_sub.dart b/test/end2end/foo_sub.dart new file mode 100644 index 00000000..9ded5584 --- /dev/null +++ b/test/end2end/foo_sub.dart @@ -0,0 +1,9 @@ +import 'foo.dart'; + +class FooSub extends Foo { + const FooSub(); +} + +class FooSub2 extends Foo { + const FooSub2(); +} diff --git a/test/end2end/generated_mocks_test.dart b/test/end2end/generated_mocks_test.dart index 39e45b5b..cb2f1b1f 100644 --- a/test/end2end/generated_mocks_test.dart +++ b/test/end2end/generated_mocks_test.dart @@ -3,6 +3,7 @@ import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; import 'foo.dart'; +import 'foo_sub.dart'; import 'generated_mocks_test.mocks.dart'; T dummyMethod() => [1, 1.5].whereType().first!; @@ -66,6 +67,24 @@ void main() { when(foo.parameterWithDefault()).thenReturn('Default'); expect(foo.parameterWithDefault(), equals('Default')); + + const foo2 = FooSub(); + when(foo.parameterWithDefault2(foo2)).thenReturn('Stubbed'); + expect(foo.parameterWithDefault2(foo2), equals('Stubbed')); + + when(foo.parameterWithDefault2()).thenReturn('Default'); + expect(foo.parameterWithDefault2(), equals('Default')); + }); + + test( + 'a method with a parameter with a default value factory redirect can be stubbed', + () { + const foo2 = FooSub2(); + when(foo.parameterWithDefaultFactoryRedirect(foo2)).thenReturn('Stubbed'); + expect(foo.parameterWithDefaultFactoryRedirect(foo2), equals('Stubbed')); + + when(foo.parameterWithDefaultFactoryRedirect()).thenReturn('Default'); + expect(foo.parameterWithDefaultFactoryRedirect(), equals('Default')); }); test('an inherited method can be stubbed', () {