You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中如何Mock FirebaseAuth的verifyPhoneNumber方法用于手机号验证功能测试

解决Mock FirebaseAuth的verifyPhoneNumber方法问题

看起来你遇到的问题是firebase_auth_mocks库的MockFirebaseAuth默认没有实现verifyPhoneNumber方法,导致调用时抛出NoSuchMethodError。别担心,我们有两种可靠的方式来Mock这个方法并触发对应的回调函数:

方法一:扩展MockFirebaseAuth手动实现verifyPhoneNumber

我们可以创建一个自定义的Mock类,继承自MockFirebaseAuth,然后手动实现verifyPhoneNumber方法,根据测试需求触发不同的回调。

import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
import 'package:firebase_auth/firebase_auth.dart';

class CustomMockFirebaseAuth extends MockFirebaseAuth {
  // 保存回调引用,方便测试时主动触发
  VerificationCallback? onVerificationCompleted;
  VerificationFailedCallback? onVerificationFailed;
  CodeSentCallback? onCodeSent;
  CodeAutoRetrievalTimeoutCallback? onCodeAutoRetrievalTimeout;

  @override
  Future<void> verifyPhoneNumber({
    required String phoneNumber,
    required VerificationCallback verificationCompleted,
    required VerificationFailedCallback verificationFailed,
    required CodeSentCallback codeSent,
    required CodeAutoRetrievalTimeoutCallback codeAutoRetrievalTimeout,
    Duration timeout = const Duration(seconds: 30),
    int? forceResendingToken,
    PhoneMultiFactorInfo? multiFactorInfo,
  }) async {
    // 保存所有回调
    onVerificationCompleted = verificationCompleted;
    onVerificationFailed = verificationFailed;
    onCodeSent = codeSent;
    onCodeAutoRetrievalTimeout = codeAutoRetrievalTimeout;

    // 这里可以预设默认触发的回调,比如默认触发codeSent
    codeSent('mock_verification_id', forceResendingToken);
  }
}

然后在测试中使用这个自定义Mock类,灵活触发不同回调:

// auth_bloc_test.dart
final mockAuth = CustomMockFirebaseAuth();
final authBloc = AuthBloc(firebaseAuth: mockAuth);

// 测试verificationCompleted场景
test('emits verificationCompleted state when callback is triggered', () {
  final mockCredential = MockPhoneAuthCredential();
  
  // 主动触发verificationCompleted回调
  mockAuth.onVerificationCompleted?.call(mockCredential);
  
  // 验证Bloc是否发出对应状态
  expectLater(
    authBloc.stream,
    emits(const AuthState.verificationCompleted(mockCredential)),
  );
});

// 测试codeSent场景
test('emits codeSent state when verifyPhoneNumber is called', () {
  expectLater(
    authBloc.stream,
    emits(const AuthState.codeSent('mock_verification_id')),
  );
  // 调用Bloc的验证方法
  authBloc._verify('1234567890');
});

方法二:使用Mockito生成Mock类

如果firebase_auth_mocks满足不了需求,我们可以直接用Mockito库生成FirebaseAuth的Mock类,灵活StubverifyPhoneNumber方法。

首先添加Mockito依赖到dev_dependencies:

dev_dependencies:
  mockito: ^5.4.0
  build_runner: ^2.4.4

然后生成Mock类:

  1. 创建firebase_mocks.dart文件:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:mockito/mockito.dart';

class MockFirebaseAuth extends Mock implements FirebaseAuth {}
class MockPhoneAuthCredential extends Mock implements PhoneAuthCredential {}
class MockUser extends Mock implements User {}
  1. 运行生成命令:
flutter pub run build_runner build

接下来在测试中StubverifyPhoneNumber方法,指定触发的回调:

import 'package:mockito/mockito.dart';
import 'your_mock_file_path.dart';

// auth_bloc_test.dart
test('handles verificationFailed error', () {
  final mockAuth = MockFirebaseAuth();
  final mockException = FirebaseAuthException(code: 'invalid-phone-number', message: 'Invalid phone number');
  final authBloc = AuthBloc(firebaseAuth: mockAuth);

  // Stub verifyPhoneNumber方法,触发verificationFailed回调
  when(mockAuth.verifyPhoneNumber(
    phoneNumber: anyNamed('phoneNumber'),
    verificationCompleted: anyNamed('verificationCompleted'),
    verificationFailed: anyNamed('verificationFailed'),
    codeSent: anyNamed('codeSent'),
    codeAutoRetrievalTimeout: anyNamed('codeAutoRetrievalTimeout'),
    timeout: anyNamed('timeout'),
    forceResendingToken: anyNamed('forceResendingToken'),
  )).thenAnswer((invocation) {
    final verificationFailed = invocation.namedArguments[#verificationFailed] as VerificationFailedCallback;
    verificationFailed(mockException);
    return Future.value();
  });

  // 调用验证方法
  authBloc._verify('invalid-phone');

  // 验证状态
  expectLater(
    authBloc.stream,
    emits(AuthState.verificationFailed(mockException)),
  );
});

// 测试codeAutoRetrievalTimeout场景
test('emits codeRetrievalTimeout state when timeout occurs', () {
  final mockAuth = MockFirebaseAuth();
  final authBloc = AuthBloc(firebaseAuth: mockAuth);

  when(mockAuth.verifyPhoneNumber(
    phoneNumber: anyNamed('phoneNumber'),
    verificationCompleted: anyNamed('verificationCompleted'),
    verificationFailed: anyNamed('verificationFailed'),
    codeSent: anyNamed('codeSent'),
    codeAutoRetrievalTimeout: anyNamed('codeAutoRetrievalTimeout'),
    timeout: anyNamed('timeout'),
    forceResendingToken: anyNamed('forceResendingToken'),
  )).thenAnswer((invocation) {
    final timeoutCallback = invocation.namedArguments[#codeAutoRetrievalTimeout] as CodeAutoRetrievalTimeoutCallback;
    timeoutCallback('mock_verification_id');
    return Future.value();
  });

  authBloc._verify('1234567890');

  expectLater(
    authBloc.stream,
    emits(const AuthState.codeRetrievalTimeout('mock_verification_id')),
  );
});

额外提示

  • 如果你想继续使用firebase_auth_mocks,可以检查库的最新版本,看看是否已经实现了verifyPhoneNumber方法,也可以给库提交Issue请求维护者添加该功能。
  • 测试Bloc时,记得在测试结束后调用authBloc.close(),避免内存泄漏。

内容的提问来源于stack exchange,提问作者Douglas Bett

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 06:22:53