Refactor builder and add dummy return value feature:

* Instead of concatenating a pile of classes, use a Library;
  this lets us start to import things properly.
* Refactor some code out of loops so it's more readable.
* Depending on return type of original methods, can generate
  dummy return values.
* Test manually-written `super.noSuchMethod` calls which pass
  a dummy return value. I don't know of any other code which
  passes two arguments to noSuchMethod, so I want this tested
  in all Dart environments.

PiperOrigin-RevId: 281056988
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7b21e7..930be48 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+## 4.1.2
+
+* Introduce experimental code-generated mocks. This is primarily to support
+  the new "Non-nullable by default" (NNBD) type system coming soon to Dart.
+* Add an optional second parameter to `Mock.noSuchMethod`. This may break
+  clients who use the Mock class in unconventional ways, such as overriding
+  `noSuchMethod` on a class which extends Mock. To fix, or prepare such code,
+  add a second parameter to such overriding `noSuchMethod` declaration.
+
 ## 4.1.1
 
 * Mark the unexported and accidentally public `setDefaultResponse` as
diff --git a/lib/src/builder.dart b/lib/src/builder.dart
index 55d7972..40652bc 100644
--- a/lib/src/builder.dart
+++ b/lib/src/builder.dart
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import 'package:analyzer/dart/constant/value.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:build/build.dart';
@@ -38,67 +39,75 @@
     final entryLib = await buildStep.inputLibrary;
     final resolver = buildStep.resolver;
 
-    final mockLibrary = buildStep.inputId.changeExtension('.mocks.dart');
+    final mockLibraryAsset = buildStep.inputId.changeExtension('.mocks.dart');
 
-    final mockClasses = <Class>[];
-
-    for (final element in entryLib.topLevelElements) {
-      final annotation = element.metadata.firstWhere(
-          (annotation) =>
-              annotation.element is ConstructorElement &&
-              annotation.element.enclosingElement.name == 'GenerateMocks',
-          orElse: () => null);
-      if (annotation == null) continue;
-      final generateMocksValue = annotation.computeConstantValue();
-      // TODO(srawlins): handle `generateMocksValue == null`?
-      final classesToMock = generateMocksValue.getField('classes');
-      if (classesToMock.isNull) {
-        throw InvalidMockitoAnnotationException(
-            'The "classes" argument has unknown types');
-      }
-      for (final classToMock in classesToMock.toListValue()) {
-        final dartTypeToMock = classToMock.toTypeValue();
-        // TODO(srawlins): Import the library which declares [dartTypeToMock].
-        // TODO(srawlins): Import all supporting libraries, used in type
-        // signatures.
-        if (dartTypeToMock == null) {
+    final mockLibrary = Library((lBuilder) {
+      for (final element in entryLib.topLevelElements) {
+        final annotation = element.metadata.firstWhere(
+            (annotation) =>
+                annotation.element is ConstructorElement &&
+                annotation.element.enclosingElement.name == 'GenerateMocks',
+            orElse: () => null);
+        if (annotation == null) continue;
+        final generateMocksValue = annotation.computeConstantValue();
+        // TODO(srawlins): handle `generateMocksValue == null`?
+        final classesToMock = generateMocksValue.getField('classes');
+        if (classesToMock.isNull) {
           throw InvalidMockitoAnnotationException(
-              'The "classes" argument includes a non-type: $classToMock');
+              'The "classes" argument has unknown types');
         }
 
-        final elementToMock = dartTypeToMock.element;
-        if (elementToMock is ClassElement) {
-          if (elementToMock.isEnum) {
-            throw InvalidMockitoAnnotationException(
-                'The "classes" argument includes an enum: '
-                '${elementToMock.displayName}');
-          }
-          // TODO(srawlins): Catch when someone tries to generate mocks for an
-          // un-subtypable class, like bool, String, FutureOr, etc.
-          mockClasses.add(_buildCodeForClass(dartTypeToMock, elementToMock));
-        } else if (elementToMock is GenericFunctionTypeElement &&
-            elementToMock.enclosingElement is FunctionTypeAliasElement) {
-          throw InvalidMockitoAnnotationException(
-              'The "classes" argument includes a typedef: '
-              '${elementToMock.enclosingElement.displayName}');
-        } else {
-          throw InvalidMockitoAnnotationException(
-              'The "classes" argument includes a non-class: '
-              '${elementToMock.displayName}');
-        }
+        _buildMockClasses(classesToMock.toListValue(), lBuilder);
       }
-    }
+    });
 
-    if (mockClasses.isEmpty) {
+    if (mockLibrary.body.isEmpty) {
       // Nothing to mock here!
       return;
     }
 
-    final emitter = DartEmitter();
-    final mockLibraryContent = DartFormatter()
-        .format(mockClasses.map((c) => c.accept(emitter)).join('\n'));
+    final emitter = DartEmitter.scoped();
+    final mockLibraryContent =
+        DartFormatter().format(mockLibrary.accept(emitter).toString());
 
-    await buildStep.writeAsString(mockLibrary, mockLibraryContent);
+    await buildStep.writeAsString(mockLibraryAsset, mockLibraryContent);
+  }
+
+  /// Build mock classes for [classesToMock], a list of classes obtained from a
+  /// `@GenerateMocks` annotation.
+  void _buildMockClasses(
+      List<DartObject> classesToMock, LibraryBuilder lBuilder) {
+    for (final classToMock in classesToMock) {
+      final dartTypeToMock = classToMock.toTypeValue();
+      // TODO(srawlins): Import the library which declares [dartTypeToMock].
+      // TODO(srawlins): Import all supporting libraries, used in type
+      // signatures.
+      if (dartTypeToMock == null) {
+        throw InvalidMockitoAnnotationException(
+            'The "classes" argument includes a non-type: $classToMock');
+      }
+
+      final elementToMock = dartTypeToMock.element;
+      if (elementToMock is ClassElement) {
+        if (elementToMock.isEnum) {
+          throw InvalidMockitoAnnotationException(
+              'The "classes" argument includes an enum: '
+              '${elementToMock.displayName}');
+        }
+        // TODO(srawlins): Catch when someone tries to generate mocks for an
+        // un-subtypable class, like bool, String, FutureOr, etc.
+        lBuilder.body.add(_buildCodeForClass(dartTypeToMock, elementToMock));
+      } else if (elementToMock is GenericFunctionTypeElement &&
+          elementToMock.enclosingElement is FunctionTypeAliasElement) {
+        throw InvalidMockitoAnnotationException(
+            'The "classes" argument includes a typedef: '
+            '${elementToMock.enclosingElement.displayName}');
+      } else {
+        throw InvalidMockitoAnnotationException(
+            'The "classes" argument includes a non-class: '
+            '${elementToMock.displayName}');
+      }
+    }
   }
 
   Class _buildCodeForClass(DartType dartType, ClassElement classToMock) {
@@ -107,7 +116,8 @@
     return Class((cBuilder) {
       cBuilder
         ..name = 'Mock$className'
-        ..extend = refer('Mock')
+        ..extend = refer('Mock', 'package:mockito/mockito.dart')
+        // TODO(srawlins): Add URI of dartType.
         ..implements.add(refer(className))
         ..docs.add('/// A class which mocks [$className].')
         ..docs.add('///')
@@ -125,15 +135,40 @@
         }
       }
       for (final method in classToMock.methods) {
-        if (method.parameters.isEmpty || method.isPrivate || method.isStatic) {
+        if (method.isPrivate || method.isStatic) {
           continue;
         }
-        cBuilder.methods.add(
-            Method((mBuilder) => _buildOverridingMethod(mBuilder, method)));
+        if (_returnTypeIsNonNullable(method) ||
+            _hasNonNullableParameter(method)) {
+          cBuilder.methods.add(
+              Method((mBuilder) => _buildOverridingMethod(mBuilder, method)));
+        }
       }
     });
   }
 
+  // TODO(srawlins): Update this logic to correctly handle non-nullable return
+  // types. Right now this logic does not seem to be available on DartType.
+  bool _returnTypeIsNonNullable(MethodElement method) {
+    var type = method.returnType;
+    if (type.isDynamic || type.isVoid) return false;
+    if (method.isAsynchronous && type.isDartAsyncFuture ||
+        type.isDartAsyncFutureOr) {
+      var typeArgument = (type as InterfaceType).typeArguments.first;
+      if (typeArgument.isDynamic || typeArgument.isVoid) {
+        // An asynchronous method which returns `Future<void>`, for example,
+        // does not need a dummy return value.
+        return false;
+      }
+    }
+    return true;
+  }
+
+  // TODO(srawlins): Update this logic to correctly handle non-nullable return
+  // types. Right now this logic does not seem to be available on DartType.
+  bool _hasNonNullableParameter(MethodElement method) =>
+      method.parameters.isNotEmpty;
+
   /// Build a method which overrides [method], with all non-nullable
   /// parameter types widened to be nullable.
   ///
@@ -150,6 +185,10 @@
       ..name = method.displayName
       ..returns = refer(method.returnType.displayName);
 
+    if (method.typeParameters != null && method.typeParameters.isNotEmpty) {
+      builder.types.addAll(method.typeParameters.map((p) => refer(p.name)));
+    }
+
     if (method.isAsynchronous) {
       builder.modifier =
           method.isGenerator ? MethodModifier.asyncStar : MethodModifier.async;
@@ -180,23 +219,60 @@
     // type checks.
     // TODO(srawlins): Handle getter invocations with `Invocation.getter`,
     // and operators???
-    // TODO(srawlins): Handle generic methods with `Invocation.genericMethod`.
     final invocation = refer('Invocation').property('method').call([
       refer('#${method.displayName}'),
       literalList(invocationPositionalArgs),
       if (invocationNamedArgs.isNotEmpty) literalMap(invocationNamedArgs),
     ]);
+    final noSuchMethodArgs = <Expression>[invocation];
+    if (_returnTypeIsNonNullable(method)) {
+      final dummyReturnValue = _dummyValue(method.returnType);
+      noSuchMethodArgs.add(dummyReturnValue);
+    }
     final returnNoSuchMethod =
-        refer('super').property('noSuchMethod').call([invocation]);
+        refer('super').property('noSuchMethod').call(noSuchMethodArgs);
 
     builder.body = returnNoSuchMethod.code;
   }
 
+  Expression _dummyValue(DartType type) {
+    if (type.isDartCoreBool) {
+      return literalFalse;
+    } else if (type.isDartCoreDouble) {
+      return literalNum(0.0);
+    } else if (type.isDartAsyncFuture || type.isDartAsyncFutureOr) {
+      var typeArgument = (type as InterfaceType).typeArguments.first;
+      return refer('Future')
+          .property('value')
+          .call([_dummyValue(typeArgument)]);
+    } else if (type.isDartCoreInt) {
+      return literalNum(0);
+    } else if (type.isDartCoreList) {
+      return literalList([]);
+    } else if (type.isDartCoreMap) {
+      return literalMap({});
+    } else if (type.isDartCoreNum) {
+      return literalNum(0);
+    } else if (type.isDartCoreSet) {
+      // This is perhaps a dangerous hack. The code, `{}`, is parsed as a Set
+      // literal if it is used in a context which explicitly expects a Set.
+      return literalMap({});
+    } else if (type.isDartCoreString) {
+      return literalString('');
+    } else {
+      // TODO(srawlins): Returning null for now, but really this should only
+      // ever get to a state where we have to make a Fake class which implements
+      // the type, and return a no-op constructor call to that Fake class here.
+      return literalNull;
+    }
+  }
+
   /// Returns a [Parameter] which matches [parameter].
   Parameter _matchingParameter(ParameterElement parameter) =>
       Parameter((pBuilder) {
         pBuilder
           ..name = parameter.displayName
+          // TODO(srawlins): Add URI of `parameter.type`.
           ..type = refer(parameter.type.displayName);
         if (parameter.isNamed) pBuilder.named = true;
         if (parameter.defaultValueCode != null) {
diff --git a/lib/src/mock.dart b/lib/src/mock.dart
index 320586e..4ec119c 100644
--- a/lib/src/mock.dart
+++ b/lib/src/mock.dart
@@ -107,20 +107,27 @@
 
   @override
   @visibleForTesting
-  dynamic noSuchMethod(Invocation invocation) {
+
+  /// Handles method stubbing, method call verification, and real method calls.
+  ///
+  /// If passed, [returnValue] will be returned during method stubbing and
+  /// method call verification. This is useful in cases where the method
+  /// invocation which led to `noSuchMethod` being called has a non-nullable
+  /// return type.
+  dynamic noSuchMethod(Invocation invocation, [Object /*?*/ returnValue]) {
     // noSuchMethod is that 'magic' that allows us to ignore implementing fields
     // and methods and instead define them later at compile-time per instance.
     // See "Emulating Functions and Interactions" on dartlang.org: goo.gl/r3IQUH
     invocation = _useMatchedInvocationIfSet(invocation);
     if (_whenInProgress) {
       _whenCall = _WhenCall(this, invocation);
-      return null;
+      return returnValue;
     } else if (_verificationInProgress) {
       _verifyCalls.add(_VerifyCall(this, invocation));
-      return null;
+      return returnValue;
     } else if (_untilCalledInProgress) {
       _untilCall = _UntilCall(this, invocation);
-      return null;
+      return returnValue;
     } else {
       _realCalls.add(RealCall(this, invocation));
       _invocationStreamController.add(invocation);
diff --git a/test/builder_test.dart b/test/builder_test.dart
index 5c24898..cc7db04 100644
--- a/test/builder_test.dart
+++ b/test/builder_test.dart
@@ -50,7 +50,7 @@
         ...simpleTestAsset,
         'foo|lib/foo.dart': dedent(r'''
         class Foo {
-          int a() => 7;
+          dynamic a() => 7;
           int _b(int x) => 8;
           static int c(int y) => 9;
         }
@@ -58,10 +58,12 @@
       },
       outputs: {
         'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
         /// A class which mocks [Foo].
         ///
         /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends Mock implements Foo {}
+        class MockFoo extends _i1.Mock implements Foo {}
         '''),
       },
     );
@@ -84,10 +86,12 @@
       },
       outputs: {
         'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
         /// A class which mocks [Foo].
         ///
         /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends Mock implements Foo {}
+        class MockFoo extends _i1.Mock implements Foo {}
         '''),
       },
     );
@@ -112,10 +116,12 @@
       },
       outputs: {
         'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
         /// A class which mocks [Foo].
         ///
         /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends Mock implements Foo {
+        class MockFoo extends _i1.Mock implements Foo {
           dynamic a(int m, String n) =>
               super.noSuchMethod(Invocation.method(#a, [m, n]));
         }
@@ -146,10 +152,12 @@
       },
       outputs: {
         'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
         /// A class which mocks [Foo].
         ///
         /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends Mock implements Foo {
+        class MockFoo extends _i1.Mock implements Foo {
           dynamic a(int m, String n) =>
               super.noSuchMethod(Invocation.method(#a, [m, n]));
           dynamic b(List<int> list) =>
@@ -188,10 +196,12 @@
       },
       outputs: {
         'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
         /// A class which mocks [Foo].
         ///
         /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends Mock implements Foo {
+        class MockFoo extends _i1.Mock implements Foo {
           dynamic a(int m, String n) =>
               super.noSuchMethod(Invocation.method(#a, [m, n]));
         }
@@ -199,7 +209,7 @@
         /// A class which mocks [Bar].
         ///
         /// See the documentation for Mockito's code generation for more information.
-        class MockBar extends Mock implements Bar {
+        class MockBar extends _i1.Mock implements Bar {
           dynamic b(List<int> list) =>
               super.noSuchMethod(Invocation.method(#b, [list]));
         }
@@ -208,7 +218,39 @@
     );
   });
 
-  test('generates a mock class and overrides getters and setters', () async {
+  test('overrides generic methods', () async {
+    await testBuilder(
+      buildMocks(BuilderOptions({})),
+      {
+        ...annotationsAsset,
+        ...simpleTestAsset,
+        'foo|lib/foo.dart': dedent(r'''
+        class Foo {
+          dynamic f<T>(int a) {}
+          // Bounded type parameters blocked by
+          // https://github.com/dart-lang/code_builder/issues/251.
+          // dynamic g<T extends Comparable>(int a) {}
+        }
+        '''),
+      },
+      outputs: {
+        // TODO(srawlins): The getter will appear when it has a non-nullable
+        // return type.
+        'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
+        /// A class which mocks [Foo].
+        ///
+        /// See the documentation for Mockito's code generation for more information.
+        class MockFoo extends _i1.Mock implements Foo {
+          dynamic f<T>(int a) => super.noSuchMethod(Invocation.method(#f, [a]));
+        }
+        '''),
+      },
+    );
+  });
+
+  test('overrides getters and setters', () async {
     await testBuilder(
       buildMocks(BuilderOptions({})),
       {
@@ -226,10 +268,12 @@
         // TODO(srawlins): The getter will appear when it has a non-nullable
         // return type.
         'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
         /// A class which mocks [Foo].
         ///
         /// See the documentation for Mockito's code generation for more information.
-        class MockFoo extends Mock implements Foo {
+        class MockFoo extends _i1.Mock implements Foo {
           set b(int value) => super.noSuchMethod(Invocation.setter(#b, [value]));
         }
         '''),
@@ -237,6 +281,74 @@
     );
   });
 
+  test('creates dummy non-null return values for known core classes', () async {
+    await testBuilder(
+      buildMocks(BuilderOptions({})),
+      {
+        ...annotationsAsset,
+        ...simpleTestAsset,
+        'foo|lib/foo.dart': dedent(r'''
+        class Foo {
+          bool m1() => false;
+          double m2() => 3.14;
+          int m3() => 7;
+          String m4() => "Hello";
+          List<Foo> m5() => [Foo()];
+          Set<Foo> m6() => {Foo()};
+          Map<int, Foo> m7() => {7: Foo()};
+        }
+        '''),
+      },
+      outputs: {
+        'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
+        /// A class which mocks [Foo].
+        ///
+        /// See the documentation for Mockito's code generation for more information.
+        class MockFoo extends _i1.Mock implements Foo {
+          bool m1() => super.noSuchMethod(Invocation.method(#m1, []), false);
+          double m2() => super.noSuchMethod(Invocation.method(#m2, []), 0.0);
+          int m3() => super.noSuchMethod(Invocation.method(#m3, []), 0);
+          String m4() => super.noSuchMethod(Invocation.method(#m4, []), '');
+          List<Foo> m5() => super.noSuchMethod(Invocation.method(#m5, []), []);
+          Set<Foo> m6() => super.noSuchMethod(Invocation.method(#m6, []), {});
+          Map<int, Foo> m7() => super.noSuchMethod(Invocation.method(#m7, []), {});
+        }
+        '''),
+      },
+    );
+  });
+
+  test('creates dummy non-null return values for Futures of known core classes',
+      () async {
+    await testBuilder(
+      buildMocks(BuilderOptions({})),
+      {
+        ...annotationsAsset,
+        ...simpleTestAsset,
+        'foo|lib/foo.dart': dedent(r'''
+        class Foo {
+          Future<bool> m1() async => false;
+        }
+        '''),
+      },
+      outputs: {
+        'foo|test/foo_test.mocks.dart': dedent(r'''
+        import 'package:mockito/mockito.dart' as _i1;
+
+        /// A class which mocks [Foo].
+        ///
+        /// See the documentation for Mockito's code generation for more information.
+        class MockFoo extends _i1.Mock implements Foo {
+          Future<bool> m1() async =>
+              super.noSuchMethod(Invocation.method(#m1, []), Future.value(false));
+        }
+        '''),
+      },
+    );
+  });
+
   test('throws when GenerateMocks references an unresolved type', () async {
     expectBuilderThrows(
       assets: {
diff --git a/test/mockito_test.dart b/test/mockito_test.dart
index 1b80150..c1f65e2 100644
--- a/test/mockito_test.dart
+++ b/test/mockito_test.dart
@@ -65,9 +65,6 @@
   }
 }
 
-String noMatchingCallsFooter = "(If you called `verify(...).called(0);`, "
-    "please instead use `verifyNever(...);`.)";
-
 void main() {
   _MockedClass mock;
 
diff --git a/test/nnbd_support_test.dart b/test/nnbd_support_test.dart
new file mode 100644
index 0000000..ed36543
--- /dev/null
+++ b/test/nnbd_support_test.dart
@@ -0,0 +1,125 @@
+// Copyright 2019 Dart Mockito authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import 'package:mockito/mockito.dart';
+import 'package:test/test.dart';
+
+class Foo {
+  String /*?*/ returnsNullableString() => 'Hello';
+
+  // TODO(srawlins): When it becomes available, opt this test library into NNBD,
+  // and make this method really return a non-nullable String.
+  String /*!*/ returnsNonNullableString() => 'Hello';
+}
+
+class MockFoo extends Mock implements Foo {
+  String /*!*/ returnsNonNullableString() {
+    return super.noSuchMethod(
+        Invocation.method(#returnsNonNullableString, []), 'Dummy');
+  }
+}
+
+void main() {
+  MockFoo mock;
+
+  setUp(() {
+    mock = MockFoo();
+  });
+
+  tearDown(() {
+    // In some of the tests that expect an Error to be thrown, Mockito's
+    // global state can become invalid. Reset it.
+    resetMockitoState();
+  });
+
+  group('Using nSM out of the box,', () {
+    test('nSM returns the dummy value during method stubbing', () {
+      // Trigger method stubbing.
+      final whenCall = when;
+      final stubbedResponse = mock.returnsNullableString();
+      expect(stubbedResponse, equals(null));
+      whenCall(stubbedResponse).thenReturn('A');
+    });
+
+    test('nSM returns the dummy value during method call verification', () {
+      when(mock.returnsNullableString()).thenReturn('A');
+
+      // Make a real call.
+      final realResponse = mock.returnsNullableString();
+      expect(realResponse, equals('A'));
+
+      // Trigger method call verification.
+      final verifyCall = verify;
+      final verificationResponse = mock.returnsNullableString();
+      expect(verificationResponse, equals(null));
+      verifyCall(verificationResponse);
+    });
+
+    test(
+        'nSM returns the dummy value during method call verification, using '
+        'verifyNever', () {
+      // Trigger method call verification.
+      final verifyNeverCall = verifyNever;
+      final verificationResponse = mock.returnsNullableString();
+      expect(verificationResponse, equals(null));
+      verifyNeverCall(verificationResponse);
+    });
+  });
+
+  group('Using a method stub which passes a return argument to nSM,', () {
+    test('nSM returns the dummy value during method stubbing', () {
+      // Trigger method stubbing.
+      final whenCall = when;
+      final stubbedResponse = mock.returnsNonNullableString();
+
+      // Under the pre-NNBD type system, this expectation is not interesting.
+      // Under the NNBD type system, however, it is important that the second
+      // argument passed to `noSuchMethod` in `returnsNonNullableString` is
+      // returned by `noSuchMethod`, so that non-null values are can be returned
+      // when necessary.
+      expect(stubbedResponse, equals('Dummy'));
+      whenCall(stubbedResponse).thenReturn('A');
+    });
+
+    test('nSM returns the dummy value during method call verification', () {
+      when(mock.returnsNonNullableString()).thenReturn('A');
+
+      // Make a real call.
+      final realResponse = mock.returnsNonNullableString();
+
+      // Under the pre-NNBD type system, this expectation is not interesting.
+      // Under the NNBD type system, however, it is important that the second
+      // argument passed to `noSuchMethod` in `returnsNonNullableString` is
+      // _not_ returned by `noSuchMethod`; the canned response, `'A'`, should be
+      // returned.
+      expect(realResponse, equals('A'));
+
+      // Trigger method call verification.
+      final verifyCall = verify;
+      final verificationResponse = mock.returnsNonNullableString();
+      expect(verificationResponse, equals('Dummy'));
+      verifyCall(verificationResponse);
+    });
+
+    test(
+        'nSM returns the dummy value during method call verification, using '
+        'verifyNever', () {
+      // Trigger method call verification.
+      final verifyNeverCall = verifyNever;
+      final verificationResponse = mock.returnsNonNullableString();
+      expect(verificationResponse, equals('Dummy'));
+      verifyNeverCall(verificationResponse);
+    });
+  });
+}