mocktail icon indicating copy to clipboard operation
mocktail copied to clipboard

Arguments with type generics are not properly verified using any()

Open lukaszdebowski opened this issue 3 years ago • 2 comments
trafficstars

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

lukaszdebowski avatar Nov 03 '22 07:11 lukaszdebowski

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 😄

alestiago avatar Jan 31 '23 15:01 alestiago

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?

bw-flagship avatar Jul 05 '23 14:07 bw-flagship

You'll need to create stub for each generic type that you care about for a given test. Hope that helps!

felangel avatar Apr 20 '24 03:04 felangel