Skip to content

Commit

Permalink
Remove deprecated returnNullOnMissingStub and `OnMissingStub.return…
Browse files Browse the repository at this point in the history
…Null`

This option results in runtime type errors and was superseeded by
`OnMissingStub.returnDefault`.

PiperOrigin-RevId: 576796424
  • Loading branch information
Ilya Yanok authored and copybara-github committed Oct 26, 2023
1 parent 4edf86f commit 1a0d0e7
Show file tree
Hide file tree
Showing 7 changed files with 15 additions and 138 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* **Potentially breaking** Changed default `String` value returned by nice
mocks' unstubbed method to include some useful info. This could break the
tests that relied on getting an empty `String` from unstubbed methods.
* Remove deprecated `returnNullOnMissingStub` and `OnMissingStub.returnNull`
`MockSpec` options.

## 5.4.2

Expand Down
13 changes: 0 additions & 13 deletions NULL_SAFETY_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,6 @@ behavior, use `@GenerateMocks`:
@GenerateMocks([Foo])
```

#### Old "return null" missing stub behavior

To use the old default behavior of returning null (which doesn't make a lot of
sense in the Null safety type system), for legacy code, use
`returnNullOnMissingStub`:

```dart
@GenerateMocks([], customMocks: [MockSpec<Foo>(returnNullOnMissingStub: true)])
```

This option is soft-deprecated (no deprecation warning yet); it will be marked
`@deprecated` in a future release, and removed in a later release.

#### Mocking members with non-nullable type variable return types

If a class has a member with a type variable as a return type (for example,
Expand Down
26 changes: 1 addition & 25 deletions lib/annotations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@ class GenerateNiceMocks {
/// The name of the mock class is either specified with the `as` named argument,
/// or is the name of the class being mocked, prefixed with 'Mock'.
///
/// To use the legacy behavior of returning null for unstubbed methods, use
/// `returnNullOnMissingStub: true`.
///
/// For example, given the generic class, `class Foo<T>`, then this
/// annotation:
///
Expand All @@ -99,10 +96,6 @@ class MockSpec<T> {

final List<Type> mixins;

@Deprecated('Specify "missing stub" behavior with the [onMissingStub] '
'parameter.')
final bool returnNullOnMissingStub;

final OnMissingStub? onMissingStub;

final Set<Symbol> unsupportedMembers;
Expand All @@ -117,12 +110,6 @@ class MockSpec<T> {
///
/// Specify a custom name with the [as] parameter.
///
/// If [onMissingStub] is specified as [OnMissingStub.returnNull],
/// (or if the deprecated parameter [returnNullOnMissingStub] is `true`), then
/// a real call to a mock method (or getter) will return `null` when no stub
/// is found. This may result in a runtime error, if the return type of the
/// method (or the getter) is non-nullable.
///
/// If [onMissingStub] is specified as
/// [OnMissingStub.returnDefault], a real call to a mock method (or
/// getter) will return a legal value when no stub is found.
Expand All @@ -149,10 +136,7 @@ class MockSpec<T> {
Symbol? as,
@Deprecated('Avoid adding concrete implementation to mock classes. '
'Use a manual implementation of the class without `Mock`')
List<Type> mixingIn = const [],
@Deprecated('Specify "missing stub" behavior with the '
'[onMissingStub] parameter.')
this.returnNullOnMissingStub = false,
List<Type> mixingIn = const [],
this.unsupportedMembers = const {},
this.fallbackGenerators = const {},
this.onMissingStub,
Expand All @@ -166,14 +150,6 @@ enum OnMissingStub {
/// An exception should be thrown.
throwException,

/// A `null` value should be returned.
///
/// This is considered legacy behavior, as it may result in a runtime error,
/// if the return type of the method (or the getter) is non-nullable.
@Deprecated(
'This is legacy behavior, it may result in runtime errors. Consider using returnDefault instead')
returnNull,

/// A legal default value should be returned.
///
/// For basic known types, like `int` and `Future<String>`, a simple value is
Expand Down
44 changes: 6 additions & 38 deletions lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -648,47 +648,15 @@ class _MockTargetGatherer {
mixins.add(mixinInterfaceType);
}

final returnNullOnMissingStub =
mockSpec.getField('returnNullOnMissingStub')!.toBoolValue()!;
final onMissingStubValue = mockSpec.getField('onMissingStub')!;
final OnMissingStub onMissingStub;
if (nice) {
// The new @GenerateNiceMocks API. Don't allow `returnNullOnMissingStub`.
if (returnNullOnMissingStub) {
throw ArgumentError("'returnNullOnMissingStub' is not supported with "
'@GenerateNiceMocks');
}
if (onMissingStubValue.isNull) {
onMissingStub = OnMissingStub.returnDefault;
} else {
final onMissingStubIndex =
onMissingStubValue.getField('index')!.toIntValue()!;
onMissingStub = OnMissingStub.values[onMissingStubIndex];
// ignore: deprecated_member_use_from_same_package
if (onMissingStub == OnMissingStub.returnNull) {
throw ArgumentError(
"'OnMissingStub.returnNull' is not supported with "
'@GenerateNiceMocks');
}
}
if (onMissingStubValue.isNull) {
onMissingStub =
nice ? OnMissingStub.returnDefault : OnMissingStub.throwException;
} else {
if (onMissingStubValue.isNull) {
// No value was given for `onMissingStub`. But the behavior may
// be specified by `returnNullOnMissingStub`.
onMissingStub = returnNullOnMissingStub
// ignore: deprecated_member_use_from_same_package
? OnMissingStub.returnNull
: OnMissingStub.throwException;
} else {
// A value was given for `onMissingStub`.
if (returnNullOnMissingStub) {
throw ArgumentError("Cannot specify 'returnNullOnMissingStub' and a "
"'onMissingStub' value at the same time.");
}
final onMissingStubIndex =
onMissingStubValue.getField('index')!.toIntValue()!;
onMissingStub = OnMissingStub.values[onMissingStubIndex];
}
final onMissingStubIndex =
onMissingStubValue.getField('index')!.toIntValue()!;
onMissingStub = OnMissingStub.values[onMissingStubIndex];
}
final unsupportedMembers = {
for (final m in mockSpec.getField('unsupportedMembers')!.toSetValue()!)
Expand Down
3 changes: 0 additions & 3 deletions test/builder/auto_mocks_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,12 @@ class GenerateMocks {
class MockSpec<T> {
final Symbol? mockName;
final bool returnNullOnMissingStub;
final Set<Symbol> unsupportedMembers;
final Map<Symbol, Function> fallbackGenerators;
const MockSpec({
Symbol? as,
this.returnNullOnMissingStub = false,
this.unsupportedMembers = const {},
this.fallbackGenerators = const {},
})
Expand Down
23 changes: 1 addition & 22 deletions test/builder/custom_mocks_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ class MockSpec<T> {
final List<Type> mixins;
final bool returnNullOnMissingStub;
final OnMissingStub? onMissingStub;
final Set<Symbol> unsupportedMembers;
Expand All @@ -54,15 +52,14 @@ class MockSpec<T> {
const MockSpec({
Symbol? as,
List<Type> mixingIn = const [],
this.returnNullOnMissingStub = false,
this.onMissingStub,
this.unsupportedMembers = const {},
this.fallbackGenerators = const {},
}) : mockName = as,
mixins = mixingIn;
}
enum OnMissingStub { throwException, returnNull, returnDefault }
enum OnMissingStub { throwException, returnDefault }
'''
};

Expand Down Expand Up @@ -438,24 +435,6 @@ void main() {
);
});

test(
'generates a mock class which uses the old behavior of returning null on '
'missing stubs', () async {
final mocksContent = await buildWithNonNullable({
...annotationsAsset,
'foo|lib/foo.dart': dedent(r'''
class Foo<T> {}
'''),
'foo|test/foo_test.dart': '''
import 'package:foo/foo.dart';
import 'package:mockito/annotations.dart';
@GenerateMocks([], customMocks: [MockSpec<Foo>(as: #MockFoo, returnNullOnMissingStub: true)])
void main() {}
'''
});
expect(mocksContent, isNot(contains('throwOnMissingStub')));
});

test(
'generates mock methods with private return types, given '
'unsupportedMembers', () async {
Expand Down
42 changes: 5 additions & 37 deletions test/end2end/generated_mocks_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ import 'generated_mocks_test.mocks.dart';
FooSub,
Bar
], customMocks: [
MockSpec<Foo>(as: #MockFooRelaxed, returnNullOnMissingStub: true),
MockSpec<Foo>(
as: #MockFooWithDefaults,
onMissingStub: OnMissingStub.returnDefault,
),
MockSpec<Bar>(as: #MockBarRelaxed, returnNullOnMissingStub: true),
MockSpec<Baz>(
as: #MockBazWithUnsupportedMembers,
unsupportedMembers: {
Expand All @@ -28,7 +26,8 @@ import 'generated_mocks_test.mocks.dart';
),
MockSpec<HasPrivate>(mixingIn: [HasPrivateMixin]),
])
@GenerateNiceMocks([MockSpec<Foo>(as: #MockFooNice)])
@GenerateNiceMocks(
[MockSpec<Foo>(as: #MockFooNice), MockSpec<Bar>(as: #MockBarNice)])
void main() {
group('for a generated mock,', () {
late MockFoo<Object> foo;
Expand Down Expand Up @@ -225,35 +224,6 @@ void main() {
});
});

group('for a generated mock using returnNullOnMissingStub,', () {
late Foo<Object> foo;

setUp(() {
foo = MockFooRelaxed();
});

test('an unstubbed method returning a non-nullable type throws a TypeError',
() {
when(foo.namedParameter(x: 42)).thenReturn('Stubbed');
expect(
() => foo.namedParameter(x: 43), throwsA(TypeMatcher<TypeError>()));
});

test('an unstubbed getter returning a non-nullable type throws a TypeError',
() {
expect(() => foo.getter, throwsA(TypeMatcher<TypeError>()));
});

test('an unstubbed method returning a nullable type returns null', () {
when(foo.nullableMethod(42)).thenReturn('Stubbed');
expect(foo.nullableMethod(43), isNull);
});

test('an unstubbed getter returning a nullable type returns null', () {
expect(foo.nullableGetter, isNull);
});
});

group('for a generated mock using OnMissingStub.returnDefault,', () {
late Foo<Object> foo;

Expand Down Expand Up @@ -356,11 +326,9 @@ void main() {
expect(foo.methodWithBarArg(bar), equals('mocked result'));
});

test(
'a generated mock which returns null on missing stubs can be used as a '
'stub argument', () {
final foo = MockFooRelaxed();
final bar = MockBarRelaxed();
test('a generated nice mock can be used as a stub argument', () {
final foo = MockFoo();
final bar = MockBarNice();
when(foo.methodWithBarArg(bar)).thenReturn('mocked result');
expect(foo.methodWithBarArg(bar), equals('mocked result'));
});
Expand Down

0 comments on commit 1a0d0e7

Please sign in to comment.