Skip to content

Commit 28395ca

Browse files
committed
Override toString implementation on generated Fakes in order to match the
signature of an overriding method which adds optional parameters. Fixes #371 PiperOrigin-RevId: 377388828
1 parent ed61909 commit 28395ca

File tree

5 files changed

+69
-34
lines changed

5 files changed

+69
-34
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
## 5.0.10-dev
1+
## 5.0.10
22

33
* Generate a proper mock class when the mocked class overrides `toString`,
44
`hashCode`, or `operator==`.
55
[#420](https://github.com/dart-lang/mockito/issues/420)
6+
* Override `toString` implementation on generated Fakes in order to match the
7+
signature of an overriding method which adds optional parameters.
8+
[#371](https://github.com/dart-lang/mockito/issues/371)
69

710
## 5.0.9
811

lib/src/builder.dart

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -907,8 +907,7 @@ class _MockLibraryInfo {
907907
if (_returnTypeIsNonNullable(method) ||
908908
_hasNonNullableParameter(method) ||
909909
_needsOverrideForVoidStub(method)) {
910-
yield Method((mBuilder) => _buildOverridingMethod(mBuilder, method,
911-
className: type.getDisplayString(withNullability: true)));
910+
yield Method((mBuilder) => _buildOverridingMethod(mBuilder, method));
912911
}
913912
}
914913
if (type.mixins != null) {
@@ -960,8 +959,7 @@ class _MockLibraryInfo {
960959
///
961960
/// This new method just calls `super.noSuchMethod`, optionally passing a
962961
/// return value for methods with a non-nullable return type.
963-
void _buildOverridingMethod(MethodBuilder builder, MethodElement method,
964-
{required String className}) {
962+
void _buildOverridingMethod(MethodBuilder builder, MethodElement method) {
965963
var name = method.displayName;
966964
if (method.isOperator) name = 'operator$name';
967965
builder
@@ -1161,41 +1159,20 @@ class _MockLibraryInfo {
11611159
}
11621160

11631161
Expression _dummyValueImplementing(analyzer.InterfaceType dartType) {
1164-
// For each type parameter on [dartType], the Mock class needs a type
1165-
// parameter with same type variables, and a mirrored type argument for the
1166-
// "implements" clause.
1167-
var typeParameters = <Reference>[];
1168-
var elementToFake = dartType.element;
1162+
final elementToFake = dartType.element;
11691163
if (elementToFake.isEnum) {
11701164
return _typeReference(dartType).property(
11711165
elementToFake.fields.firstWhere((f) => f.isEnumConstant).name);
11721166
} else {
11731167
// There is a potential for these names to collide. If one mock class
11741168
// requires a fake for a certain Foo, and another mock class requires a
11751169
// fake for a different Foo, they will collide.
1176-
var fakeName = '_Fake${elementToFake.name}';
1170+
final fakeName = '_Fake${elementToFake.name}';
11771171
// Only make one fake class for each class that needs to be faked.
11781172
if (!fakedClassElements.contains(elementToFake)) {
1179-
fakeClasses.add(Class((cBuilder) {
1180-
cBuilder
1181-
..name = fakeName
1182-
..extend = referImported('Fake', 'package:mockito/mockito.dart');
1183-
if (elementToFake.typeParameters != null) {
1184-
for (var typeParameter in elementToFake.typeParameters) {
1185-
cBuilder.types.add(_typeParameterReference(typeParameter));
1186-
typeParameters.add(refer(typeParameter.name));
1187-
}
1188-
}
1189-
cBuilder.implements.add(TypeReference((b) {
1190-
b
1191-
..symbol = elementToFake.name
1192-
..url = _typeImport(elementToFake)
1193-
..types.addAll(typeParameters);
1194-
}));
1195-
}));
1196-
fakedClassElements.add(elementToFake);
1197-
}
1198-
var typeArguments = dartType.typeArguments;
1173+
_addFakeClass(fakeName, elementToFake);
1174+
}
1175+
final typeArguments = dartType.typeArguments;
11991176
return TypeReference((b) {
12001177
b
12011178
..symbol = fakeName
@@ -1204,6 +1181,41 @@ class _MockLibraryInfo {
12041181
}
12051182
}
12061183

1184+
/// Adds a [Fake] implementation of [elementToFake], named [fakeName].
1185+
void _addFakeClass(String fakeName, ClassElement elementToFake) {
1186+
fakeClasses.add(Class((cBuilder) {
1187+
// For each type parameter on [elementToFake], the Fake class needs a type
1188+
// parameter with same type variables, and a mirrored type argument for
1189+
// the "implements" clause.
1190+
final typeParameters = <Reference>[];
1191+
cBuilder
1192+
..name = fakeName
1193+
..extend = referImported('Fake', 'package:mockito/mockito.dart');
1194+
if (elementToFake.typeParameters != null) {
1195+
for (var typeParameter in elementToFake.typeParameters) {
1196+
cBuilder.types.add(_typeParameterReference(typeParameter));
1197+
typeParameters.add(refer(typeParameter.name));
1198+
}
1199+
}
1200+
cBuilder.implements.add(TypeReference((b) {
1201+
b
1202+
..symbol = elementToFake.name
1203+
..url = _typeImport(elementToFake)
1204+
..types.addAll(typeParameters);
1205+
}));
1206+
1207+
final toStringMethod = elementToFake.methods
1208+
.firstWhereOrNull((method) => method.name == 'toString');
1209+
if (toStringMethod != null) {
1210+
// If [elementToFake] includes an overriding `toString` implementation,
1211+
// we need to include an implementation which matches the signature.
1212+
cBuilder.methods.add(Method(
1213+
(mBuilder) => _buildOverridingMethod(mBuilder, toStringMethod)));
1214+
}
1215+
}));
1216+
fakedClassElements.add(elementToFake);
1217+
}
1218+
12071219
/// Returns a [Parameter] which matches [parameter].
12081220
///
12091221
/// If [parameter] is unnamed (like a positional parameter in a function

lib/src/version.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
const packageVersion = '5.0.9';
1+
const packageVersion = '5.0.10';

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: mockito
2-
version: 5.0.9
2+
version: 5.0.10
33

4-
description: A mock framework inspired by Mockito. With APIs for Fakes, Mocks, behavior verification, stubbing, and much more.
4+
description: A mock framework inspired by Mockito. With APIs for Fakes, Mocks, behavior verification, stubbing, and much more.
55
homepage: https://github.com/dart-lang/mockito
66

77
environment:

test/builder/auto_mocks_test.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2224,6 +2224,26 @@ void main() {
22242224
);
22252225
});
22262226

2227+
test('generates a fake class with an overridden `toString` implementation',
2228+
() async {
2229+
await expectSingleNonNullableOutput(
2230+
dedent('''
2231+
class Foo {
2232+
Bar m1() => Bar('name1');
2233+
}
2234+
class Bar {
2235+
String toString({bool a = true}) => '';
2236+
}
2237+
'''),
2238+
_containsAllOf(dedent('''
2239+
class _FakeBar extends _i1.Fake implements _i2.Bar {
2240+
@override
2241+
String toString({bool? a = true}) => super.toString();
2242+
}
2243+
''')),
2244+
);
2245+
});
2246+
22272247
test('deduplicates fake classes', () async {
22282248
var mocksContent = await buildWithSingleNonNullableSource(dedent(r'''
22292249
class Foo {

0 commit comments

Comments
 (0)