felangel / mocktail

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

Used on a non-mocktail object #235

Closed gummz closed 5 months ago

gummz commented 5 months ago

Hi, I'm new to mocktail and I was trying to create some tests for my analytics wrapper around FirebaseAnalytics.

I'm getting a "used on a non-mocktail object" error on the call to verify. What am I doing wrong?

I saw this answer to a previous issue but I don't understand how my usage is different from that.


class AnalyticsService {
  final _analytics = Injector.get<FirebaseAnalytics>();

  Future<void> logEvent(String eventName, Map<String, dynamic> parameters,
      bool enableAnalytics) async {
    logEventConditional(eventName, parameters, enableAnalytics,
        () => _analytics.logEvent(name: eventName, parameters: parameters));
  }

  void logEventConditional(String eventName, Map<String, dynamic> parameters,
      bool enableAnalytics, Future<void> Function() callback) async {
    if (enableAnalytics) {
      await callback();
    }
  }
}

and the test:

class MockFirebaseAnalytics extends Mock implements FirebaseAnalytics {
  @override
  Future<void> logEvent(
      {AnalyticsCallOptions? callOptions,
      required String name,
      Map<String, Object?>? parameters}) async {
    return Future.value();
  }
}

void main() {
  group('AnalyticsService Tests', () {
    late MockFirebaseAnalytics mockAnalytics;
    late AnalyticsService analyticsService;

    setUpAll(() {
      mockAnalytics = MockFirebaseAnalytics();
      GetIt.I.registerSingleton<FirebaseAnalytics>(mockAnalytics);
      analyticsService = AnalyticsService();
    });

    test(
        'logEvent should trigger logEventConditional exactly once if enableAnalytics is true',
        () async {
      final eventName = 'test_event';
      final parameters = {'param1': 'value1'};

      // Act
      await analyticsService.logEvent(eventName, parameters, true);

      // Assert
      verify(() => mockAnalytics
          .logEvent(name: 'test', parameters: {'test': 'test'})).called(1);
    });
}
felangel commented 5 months ago

@gummz you shouldn't override methods in your mock class. Instead use when to stub the method:

class MockFirebaseAnalytics extends Mock implements FirebaseAnalytics {}

void main() {
  group('AnalyticsService Tests', () {
    late MockFirebaseAnalytics mockAnalytics;
    late AnalyticsService analyticsService;

    setUpAll(() {
      mockAnalytics = MockFirebaseAnalytics();
      GetIt.I.registerSingleton<FirebaseAnalytics>(mockAnalytics);
      analyticsService = AnalyticsService();
    });

    test(
        'logEvent should trigger logEventConditional exactly once if enableAnalytics is true',
        () async {
      final eventName = 'test_event';
      final parameters = {'param1': 'value1'};

      // Stub the `logEvent` method.
      when(() => mockAnalytics.logEvent(name: any(named: 'name'), parameters: any(named: 'parameters')).thenAnswer((_) async {});

      // Act
      await analyticsService.logEvent(eventName, parameters, true);

      // Assert
      verify(() => mockAnalytics
          .logEvent(name: 'test', parameters: {'test': 'test'})).called(1);
    });
}

Hope that helps, closing for now!