如何在flutter中模拟firebase_messaging?

Ken*_*nio 7 mocking mockito dart flutter flutter-test

您好,我试图模拟 firebase 消息传递以获取令牌,但是当我尝试测试时出现一些错误,有人可以帮助我解决此错误。该错误仅在测试时出现,在我的模拟器或手机中不会出现。这是我的setupFirebaseAuthMocks。谢谢

我的测试

Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  await Firebase.initializeApp();
}

void main() {
 setupFirebaseAuthMocks();
 late ProviderContainer container;

 group('AuthenticationControllerTest -', () {
   setUpAll(() async {
     await Firebase.initializeApp();
     FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
     registerThirdPartyServices();
   });
   tearDown(() {
    unregisterThirdPartyServices();
     //container.dispose();
});
    });
Run Code Online (Sandbox Code Playgroud)

错误

MissingPluginException(No implementation found for method Messaging#getToken on channel plugins.flutter.io/firebase_messaging)
Run Code Online (Sandbox Code Playgroud)

这是我试图调用的方法

  Future<Result<Failure, bool>> registerUserFirebaseToken() async {
   try {
    log.i('Registering Firebase');
    final fireBaseMessaging = FirebaseMessaging.instance;
    final token = await fireBaseMessaging.getToken();
    log.v('Firebase token: $token');

  await api.post(
    link: '${env.getValue(kAuthUrl)}users/auth/firebase',
    body: {'token': token},
    hasHeader: true,
  );

  return const Success(true);
} catch (e) {
  return Error(Failure(message: 'Firebase registration went wrong, Please try again!', content: e.toString()));
}
Run Code Online (Sandbox Code Playgroud)

}

FDu*_*hen 3

对于那些遇到同样问题的人,官方 firebase 消息传递 Github 上有一个 Mock 示例

根据您的 Mockito 版本,您可能需要稍微更新此代码。
这是我正在使用的模拟文件Mockito v5.3.2

// ignore_for_file: require_trailing_commas

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart';
import 'package:firebase_messaging_platform_interface/firebase_messaging_platform_interface.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

typedef Callback = Function(MethodCall call);

final MockFirebaseMessaging kMockMessagingPlatform = MockFirebaseMessaging();

Future<T> neverEndingFuture<T>() async {
  // ignore: literal_only_boolean_expressions
  while (true) {
    await Future.delayed(const Duration(minutes: 5));
  }
}

void setupFirebaseMessagingMocks() {
  TestWidgetsFlutterBinding.ensureInitialized();

  setupFirebaseCoreMocks();

  // Mock Platform Interface Methods
  // ignore: invalid_use_of_protected_member
  when(kMockMessagingPlatform.delegateFor(app: anyNamed('app')))
      .thenReturn(kMockMessagingPlatform);
  // ignore: invalid_use_of_protected_member
  when(kMockMessagingPlatform.setInitialValues(
    isAutoInitEnabled: anyNamed('isAutoInitEnabled'),
  )).thenReturn(kMockMessagingPlatform);
}

// Platform Interface Mock Classes

// FirebaseMessagingPlatform Mock
class MockFirebaseMessaging extends Mock
    with MockPlatformInterfaceMixin
    implements FirebaseMessagingPlatform {
  MockFirebaseMessaging() {
    TestFirebaseMessagingPlatform();
  }

  @override
  bool get isAutoInitEnabled {
    return super.noSuchMethod(Invocation.getter(#isAutoInitEnabled),
        returnValue: true, returnValueForMissingStub: true) as bool;
  }

  @override
  FirebaseMessagingPlatform delegateFor({FirebaseApp? app}) {
    return super.noSuchMethod(
      Invocation.method(#delegateFor, [], {#app: app}),
      returnValue: TestFirebaseMessagingPlatform(),
      returnValueForMissingStub: TestFirebaseMessagingPlatform(),
    ) as FirebaseMessagingPlatform;
  }

  @override
  FirebaseMessagingPlatform setInitialValues({bool? isAutoInitEnabled}) {
    return super.noSuchMethod(
      Invocation.method(
          #setInitialValues, [], {#isAutoInitEnabled: isAutoInitEnabled}),
      returnValue: TestFirebaseMessagingPlatform(),
      returnValueForMissingStub: TestFirebaseMessagingPlatform(),
    ) as FirebaseMessagingPlatform;
  }

  @override
  Future<RemoteMessage?> getInitialMessage() {
    return super.noSuchMethod(Invocation.method(#getInitialMessage, []),
            returnValue: neverEndingFuture<RemoteMessage>(),
            returnValueForMissingStub: neverEndingFuture<RemoteMessage>())
        as Future<RemoteMessage?>;
  }

  @override
  Future<void> deleteToken() {
    return super.noSuchMethod(Invocation.method(#deleteToken, []),
        returnValue: Future<void>.value(),
        returnValueForMissingStub: Future<void>.value()) as Future<void>;
  }

  @override
  Future<String?> getAPNSToken() {
    return super.noSuchMethod(Invocation.method(#getAPNSToken, []),
        returnValue: Future<String>.value(''),
        returnValueForMissingStub: Future<String>.value('')) as Future<String?>;
  }

  @override
  Future<String> getToken({String? vapidKey}) {
    return super.noSuchMethod(
        Invocation.method(#getToken, [], {#vapidKey: vapidKey}),
        returnValue: Future<String>.value(''),
        returnValueForMissingStub: Future<String>.value('')) as Future<String>;
  }

  @override
  Future<void> setAutoInitEnabled(bool? enabled) {
    return super.noSuchMethod(Invocation.method(#setAutoInitEnabled, [enabled]),
        returnValue: Future<void>.value(),
        returnValueForMissingStub: Future<void>.value()) as Future<void>;
  }

  @override
  Stream<String> get onTokenRefresh {
    return super.noSuchMethod(
      Invocation.getter(#onTokenRefresh),
      returnValue: const Stream<String>.empty(),
      returnValueForMissingStub: const Stream<String>.empty(),
    ) as Stream<String>;
  }

  @override
  Future<NotificationSettings> requestPermission(
      {bool? alert = true,
      bool? announcement = false,
      bool? badge = true,
      bool? carPlay = false,
      bool? criticalAlert = false,
      bool? provisional = false,
      bool? sound = true}) {
    return super.noSuchMethod(
            Invocation.method(#requestPermission, [], {
              #alert: alert,
              #announcement: announcement,
              #badge: badge,
              #carPlay: carPlay,
              #criticalAlert: criticalAlert,
              #provisional: provisional,
              #sound: sound
            }),
            returnValue: neverEndingFuture<NotificationSettings>(),
            returnValueForMissingStub:
                neverEndingFuture<NotificationSettings>())
        as Future<NotificationSettings>;
  }

  @override
  Future<void> subscribeToTopic(String? topic) {
    return super.noSuchMethod(Invocation.method(#subscribeToTopic, [topic]),
        returnValue: Future<void>.value(),
        returnValueForMissingStub: Future<void>.value()) as Future<void>;
  }

  @override
  Future<void> unsubscribeFromTopic(String? topic) {
    return super.noSuchMethod(Invocation.method(#unsubscribeFromTopic, [topic]),
        returnValue: Future<void>.value(),
        returnValueForMissingStub: Future<void>.value()) as Future<void>;
  }
}

class TestFirebaseMessagingPlatform extends FirebaseMessagingPlatform {
  TestFirebaseMessagingPlatform() : super();
}

Run Code Online (Sandbox Code Playgroud)

这是单元测试本身

void main() {

 setupFirebaseMessagingMocks();

  setUpAll(() async {
    await Firebase.initializeApp();
    FirebaseMessagingPlatform.instance = kMockMessagingPlatform;
  });

  test('An example of test', () {
      //...
      when(kMockMessagingPlatform.getToken(vapidKey: anyNamed('vapidKey')))
          .thenAnswer(
        (_) => Future.value('DEVICE_ID'),
      );
      //...
  });
}
Run Code Online (Sandbox Code Playgroud)