Dart: type 'Null' is not a subtype of type 'Future<String?>' in Mockito

Viewed 15546

The code below used to work before null safety, but now I get "type 'Null' is not a subtype of type 'Future<String?>'" and I have absolutely no idea why and what to do. Please help, this should be really easy (except for me), because you just have to copy the code and run it as test to get the exception:

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

class MockMethodChannel extends Mock implements MethodChannel {}

void main() {

  group('all', () {

      test('test', () async {
        final mockMethodChannel = MockMethodChannel();
        when(mockMethodChannel
            .invokeMethod<String>("GET com.eight/preference-management/preferences"))
            .thenAnswer((_) async => "test");
      });
  });
}
3 Answers

Your signature for MethodChannel.invokeMethod function says it returns a Future<String?> but since the MockMethodChannel doesnot have any implementation for invokeMethod() , it will return null; dart's null safety gets angry because you lied. For a quick fix, the invokeMethod can have a return type of Future<String?>?. When you do so, you are saying null safety shouldn't bother even if the returned value is null.

However this is not a permanent solution I just want you to understand the problem.

you could add build_runner in your dev_dependencies: in pubspec.yaml

dart pub add build_runner --dev

and modify your code to

import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
//modified
import 'package:mockito/annotations.dart';
import 'generated file.dart';

class MockMethodChannel extends Mock implements MethodChannel {}

//modified
@GenerateMocks([MockMethodChannel])
void main() {

  group('all', () {

      test('test', () async {
        final mockMethodChannel = MockMockMethodChannel();
        when(mockMethodChannel
            .invokeMethod<String>("GET com.eight/preference-management/preferences"))
            .thenAnswer((_) async => "test");
      });
  });
}

and run flutter pub run build_runner build --delete-conflicting-outputs for build runner to build the stub files for you

if you want a simple way just do like this

   class WorkerAPI {
         late ApiHelper _helper;
        
          WorkerAPI() {
            _helper = GetIt.I<ApiHelper>();
          }
        
          Future<Result<List<WorkerModel>>> getWorkers() async {
            return await _helper.get(
                url: urls.workers,
                onSuccess: (response) {
                  Iterable listJson = json.decode(response.body);
                  List<WorkerModel> list =
                      listJson.map((e) => WorkerModel.fromJson(e)).toList();
                  return list;
                });
          }
    
     // override your method in mock with Non-nullable return type
    
      class WorkerApiMock extends Mock implements WorkerAPI {
          @override
          Future<Result<List<WorkerModel>>> getWorkers() {
             return super.noSuchMethod(Invocation.method(#getWorkers,null),
             returnValue:Future.value(Result.success(List.filled(0,WorkerModel()))));
        }
    
    }

if you want check this => https://github.com/dart-lang/mockito/blob/master/test/manual_mocks_test.dart

If you are using Flutter with null safety, then the way to create a mock class is different, you need to use @GenerateMocks annotation now.

Please read documentation on null safety.

Related