mocktail
mocktail copied to clipboard
Arguments with type generics are not properly verified using any()
Describe the bug I have the following code:
class Service {
Future<void> method<T>(Argument<T> argument) async {
return;
}
}
class Argument<T> extends Equatable {
Argument(this.value);
final T value;
@override
List<Object?> get props => [value];
}
class MockService extends Mock implements Service {}
and then the following test:
test('should verify the call to method', () async {
registerFallbackValue(Argument(''));
final service = MockService();
when(() => service.method(Argument('value'))).thenAnswer((_) async {});
await service.method(Argument('value'));
verify(() => service.method(any())).called(1);
});
This test throws an error
No matching calls. All calls: MockService.method<String>(Argument<String>(value))
(If you called `verify(...).called(0);`, please instead use `verifyNever(...);`.)
If I change the test to instead verify like that:
verify(() => service.method(Argument('value'))).called(1);
It starts matching the call correctly.
I would imagine matcher any() should match the same way as Argument('value').
Also, the issue goes away if I remove the type generic parameter from the service's method declaration.
If I try to change the mocking to
when(() => service.method(any())).thenAnswer((_) async {});
Then I get this error
type 'Null' is not a subtype of type 'Future<void>'
To Reproduce Run the tests with the provided code.
Expected behavior
verify(() => service.method(any())).called(1); should correctly match the call to the mocked service method.
Additional context Running on flutter stable 3.3.6, with the latest mocktail version 0.3.0
Thanks for opening an issue @lukaszdebowski .
I believe this is happening when the generic type of any (or method) can't be inferred.
Sample issue
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
class _MockFoo extends Mock implements _Foo {}
class _Foo {
T doSomething<T>(T value) => value;
}
void main() {
setUp(() {
registerFallbackValue(1);
});
test('should verify the call to method', () async {
final foo = _MockFoo();
// The type of any can't be inferred.
when(() => foo.doSomething(any())).thenAnswer((_) => 1);
foo.doSomething(1);
verify(() => foo.doSomething(any())).called(1);
});
}
You need to specify the generic type, either to the any<T>() or to the method being stubbed doSomething<T> so it can be inferred.
With any generic
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';
class _MockFoo extends Mock implements _Foo {}
class _Foo {
T doSomething<T>(T value) => value;
}
void main() {
setUp(() {
registerFallbackValue(1);
});
test('should verify the call to method', () async {
final foo = _MockFoo();
// Generic is now inferred.
when(() => foo.doSomething(any<int>())).thenAnswer((_) => 1);
foo.doSomething(1);
verify(() => foo.doSomething(any<int>())).called(1);
});
}
Let me know if this helps 😄
What can we do if the type T is not used in the arguments? Example:
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
class _MockFoo extends Mock implements _Foo {}
class _Foo {
void doSomething<T>() {
// Does something
}
}
void main() {
setUp(() {
registerFallbackValue(1);
});
test('should verify the call to method', () async {
final foo = _MockFoo();
var count = 0;
when(() => foo.doSomething<dynamic>()).thenAnswer((_) {
print("Mock is doing something");
count++;
});
foo.doSomething<String>();
foo.doSomething<int>();
expect(count, 2); // reality: 0
});
}
The only option is to call when(()... for every possible type. It would be great if I would have to call it only once. In my case, it is for hive boxes (openBox<T>). Do i miss something?
You'll need to create stub for each generic type that you care about for a given test. Hope that helps!