Can dart Mockito mock abstract classes?

Viewed 1330

I am trying to add some unit tests to my dart/flutter library, and one of the dependencies of my unit-under-test is an abstract class:

abstract class LoginService {
    Future<void> login(String username, String password);
}

When I add @GenerateMocks([LoginService]) to my test file and run build_runner, it fails with:

mockito:mockBuilder on test/login_test.dart:  Invalid @GenerateMocks annotation: The GenerateMocks "classes" argument is missing, includes an unknown type, or includes an extension 

But if I manually type:

class MockLoginService extends Mock implements LoginService {}

there is no compilation error.

If I change LoginService to no longer be abstract (with some dummy implementation), then the mock succeeds.

Any ideas? I've checked dart Mockito's GitHub issues but can't find anyone with this issue.

2 Answers

I used mockito to mock for an abstract repository. E.g;

@GenerateMocks([NumberTriviaRepository])
void main(){
  final mockNumberTriviaRepository = MockNumberTriviaRepository();
}

Use build_runner to generate files;

flutter packages pub run build_runner build --delete-conflicting-outputs

If the example code from the mockito_builder repo is any indication, it seems like in order to build mocks of abstract classes, there needs to be a concrete implementation of that class somewhere.

My guess is that, when the Dart code is compiled (JIT or otherwise) and loaded into the build_runner, tree-shaking will see that the abstract class has no implementations nor any other references of any kind, so it removes the class from the compiled code. By the time build_runner can do its thing, the abstract class effectively no longer exists.

So in theory, all you need to do is make a placeholder implementation of your class and the code generation will work:

abstract class LoginService {
  Future<void> login(String username, String password);
}

class LoginServiceImpl extends LoginService {
  @override
  Future<void> login(String username, String password) {
    throw UnimplementedError();
  }
}
Related