From 32c923f44bc74972b8fc05a579db9ff32fbec8b5 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Tue, 3 Aug 2021 13:54:42 +0200 Subject: [PATCH 1/2] Fix wrong import for method parameters with default value factories * add tests * gitignore build folder and generated mock test files --- .gitignore | 4 ++++ lib/src/builder.dart | 13 +++++++++---- test/end2end/foo.dart | 11 +++++++++-- test/end2end/foo_sub.dart | 9 +++++++++ test/end2end/generated_mocks_test.dart | 19 +++++++++++++++++++ 5 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 test/end2end/foo_sub.dart 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..c1274733 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!; @@ -1252,7 +1253,8 @@ class _MockClassInfo { /// /// 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 +1320,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', () { From 51b5d26e1bd07737a44b0af160ad04bbcda95e5f Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Fri, 6 Aug 2021 12:29:55 +0200 Subject: [PATCH 2/2] Document new `parameter` parameter --- lib/src/builder.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/builder.dart b/lib/src/builder.dart index c1274733..b950046b 100644 --- a/lib/src/builder.dart +++ b/lib/src/builder.dart @@ -1249,7 +1249,8 @@ 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.