mockito
mockito copied to clipboard
Mockito and compatibility with generic methods
Related issue was https://github.com/dart-lang/mockito/issues/152.
In Dart2, it is impossible to implement generic methods (correctly) with Mockito.
Specifically:
- It isn't possible to match on type arguments, which are part of the signature:
abstract class Fetcher {
T fetchOfType<T>();
}
class MockFetcher extends Mock implements Fetcher {}
void main() {
var fetcher = new MockFetcher();
// ERROR: Invalid syntax, `any` is not a type variable.
when(fetcher.fetchOfType<any>()).thenAnswer((_) => /* ... */);
}
- It isn't possible to return reified generics (without using mirrors):
abstract class Cat { /* ... */ }
abstract class Request<T> {
T get responseForTesting;
}
abstract class RpcHandler {
Future<T> handle<T>(Request<T> request);
}
class MockRpcHandler extends Mock implements RpcHandler {}
void main(){
var rpcHandler = new MockRpcHandler();
when(rpcHandler.handle(any)).thenAnswer((i) {
final response = (i.positionalArguments.first as Request).responseForTesting;
return new Future.value(response);
});
// ERROR: Future<dynamic> is not type of Future<Cat>.
var cat = rpcHandler.handle(new TestRequest<Cat>());
}
Unfortunately today we give no helpful hints or documentation. I think we should change this, and in default, in Mockito 4, make it impossible to mock a method with one or more generic type arguments (at least by default). We could start with making this opt-in:
void main() {
Mock.throwOnGenericStub = true;
}
This was just hit (critically) internally, and hidden by DDC whitelisting :(
One extra itch I hit internally, the latter example can be example if someone writes:
when(rpcHandler.any(any)).thenAnswer((_) => new Future.value(new Cat());
... so we might not want to ban this, perhaps this should just be documented cleanly.
Is this related to this issue?
import 'package:mockito/mockito.dart';
typedef MyHandler<T>(T arg1);
defaultHandler<T>(T arg1) {}
class Foo {
bar<T>({MyHandler<T> arg: defaultHandler}) {}
}
class MockMandrillClient extends Mock implements Foo {}
main() {}
When trying to run this "test", I get this exception:
Failed to load "/Volumes/CaseFS/mandrill-api/test/foo_test.dart":
Unable to spawn isolate: Type parameter T is not indexed
#0 TypeParameterIndexer.[] (package:kernel/binary/ast_to_binary.dart:2073)
#1 BinaryPrinter.visitTypeParameterType (package:kernel/binary/ast_to_binary.dart:1703)
#2 TypeParameterType.accept (package:kernel/ast.dart:5035)
#3 BinaryPrinter.writeNode (package:kernel/binary/ast_to_binary.dart:246)
#4 LimitedBinaryPrinter.writeNode (package:kernel/binary/limited_ast_to_binary.dart:55)
#5 BinaryPrinter.writeNodeList (package:kernel/binary/ast_to_binary.dart:238)
#6 BinaryPrinter.visitInstantiation (package:kernel/binary/ast_to_binary.dart:1332)
#7 Instantiation.accept (package:kernel/ast.dart:2959)
[...]
Without mockito, this code works fine.
@enyo That is a head scratcher; can you do a dart --version
? Are you running pub run test
or dart my_test.dart
? What version of mockito (should be in pubspec.lock
)?
@srawlins I'm running dart 2.0.0
:
/usr/local/Cellar/dart/2.0.0/libexec/bin/dart
mockito v3.0.0
It's quite interesting, because when you remove implements Foo
from the class definition it also doesn't fail.
For better or worse, @enyo, this is a Dart SDK bug. https://github.com/dart-lang/sdk/issues/34122
I thought that was very likely. Thanks for looking into it!
Hi, how are you guys doing with this issue?
I actually ran into the same problem, I'm using this https://pub.dev/packages/injector and apparently mock classes is having problems with generic types. For instance, if I ran
injector.getDependency<A>
and
injector.getDependency<B>
it would return the same object (the first object type alphabetically) because mock's inability to match generic type arguments.
We haven't looked into this issue in a long time. It just doesn't seem to crop up. An injector does sound like a case where it might be warranted.
One tricky catch to fixing this issue: it will be a breaking change. Where currently a method call would be matched, it may no longer, due to unequal type arguments.
Potentially off topic tip: A dependency injector is something you really don't want to mock. There shouldn't be any behavior that is tied to something expensive or unstable, and it should be safe to set up a real injector within a test, and shouldn't have the type of side effects to warrant a verify
that certain methods were called. https://github.com/dart-lang/mockito#best-practices
This is supported in v0.3.0 of mocktail in case that helps anyone.