felangel / mocktail

A mock library for Dart inspired by mockito
https://pub.dev/packages/mocktail
MIT License
617 stars 81 forks source link

Matcher is not working properly is specific case due to Object? #217

Closed elkSal closed 5 months ago

elkSal commented 10 months ago

Hi all,

thanks a lot for this great library, it saved me so much time with testing and made testing more enjoyable :P

I am facing the following issue: I am defining what a mock object specific function should perform with the when method but the function doesn't run. I assume the matcher doesn't consider the two functions equal.

I think it might be due to the GoRouter pushNamed extra parameter (the type is "Object?") but even adding the any(named: "extra") doesn't help. I tried without any, with any(named: "extra") and even any(named: "extra", that: isA<Object?>()) with no success.

To Reproduce Class

class ShowHcaptcha {
  final GoRouter _router;
  ShowHcaptcha(this._router);
  Future<String> call() async {
    String? hCaptchaToken =
        await _router.pushNamed(HCaptchaDialog.routeName, extra: null);
    if (hCaptchaToken == null) {
      throw HcaptchaFailure();
    } else {
      return hCaptchaToken;
    }
  }
}

Test Failing

class MockGoRouter extends Mock implements GoRouter {}

void main() {
  late MockGoRouter mockGoRouter;
  late ShowHcaptcha showHcaptcha;

  String captchaResult = "testResult";

  setUp(() {
    mockGoRouter = MockGoRouter();
    showHcaptcha = ShowHcaptcha(mockGoRouter);
  });

  setUpAll(() {
    registerFallbackValue(null);
  });
  test('should push to HCaptchaDialog & return the result on success',
      () async {
    //assign

    when(
      () => mockGoRouter.pushNamed(HCaptchaDialog.routeName,
          extra: any(named: "extra", that: isA<Object?>())),
    ).thenAnswer((invocation) async => captchaResult);
    //act
    final result = await showHcaptcha();
    //assert
    expect(result, captchaResult);
    verify(
      () => mockGoRouter.pushNamed(HCaptchaDialog.routeName),
    ).called(1);
    verifyNoMoreInteractions(mockGoRouter);
  });
}

On another usecase I used the Mocktail when function for a similar case and it worked fine. Successful test:

when(
      () => mockRouter.pushNamed(any(), extra: newEmailUser.email),
    ).thenAnswer((invocation) async {
      return null;
    });

Error thrown by failing test: type 'Null' is not a subtype of type 'Future<String?>' test/features/auth/domain/usecases/show_hcaptcha_test.dart 7:7 MockGoRouter.pushNamed package:athlas/features/auth/domain/usecases/show_hcaptcha.dart 17:23 ShowHcaptcha.call test/features/auth/domain/usecases/show_hcaptcha_test.dart 32:38 main.

elkSal commented 10 months ago

Solved it, what was causing the issue was this line in the class code

String? hCaptchaToken =
        await _router.pushNamed(HCaptchaDialog.routeName);

Working solution is:

final hCaptchaToken =
        await _router.pushNamed(HCaptchaDialog.routeName);

Does anyone know why defining the return value type causes such issue? Thanks

felangel commented 5 months ago

I'm guessing it's because of the generic type not matching. Glad you figured it out!