You need to enable JavaScript to run this app. Flutter单元测试:用Mockito桩化并触发Firebase电话认证方法回调
优惠活动
大模型
产品
解决方案
定价
更多

Flutter单元测试:用Mockito桩化并触发Firebase电话认证方法回调

Mock Firebase Phone Auth 单测中手动触发回调的实现方案

核心原理

Mockito的.thenAnswer()回调中拿到的Invocation对象,完整记录了被测方法调用verifyPhoneNumber时传入的所有命名参数——你不需要凭空构造回调,直接从调用参数里取出业务代码传入的真实回调函数,传入测试用的假参数手动执行,就能精准触发对应分支的逻辑。

关键实现步骤

  • 从invocation.namedArguments中通过参数对应的Symbol(写法为#参数名)取出对应回调
  • 按照回调的入参要求构造测试用的假对象(比如测试用的FirebaseAuthException、PhoneAuthCredential)
  • 手动调用取出的回调函数,执行业务代码中对应的分支逻辑
  • 给桩方法返回一个已完成的Future.value(),避免业务代码中await verifyPhoneNumber(...)永久挂起导致测试超时

典型分支完整测试代码示例

1. 覆盖verificationFailed(验证失败)分支

test('验证失败时正确更新错误状态', () async {
  // 配置verifyPhoneNumber的mock桩
  when(mockFirebaseAuth.verifyPhoneNumber(
    phoneNumber: anyNamed('phoneNumber'),
    timeout: anyNamed('timeout'),
    codeSent: anyNamed('codeSent'),
    verificationCompleted: anyNamed('verificationCompleted'),
    verificationFailed: anyNamed('verificationFailed'),
    codeAutoRetrievalTimeout: anyNamed('codeAutoRetrievalTimeout'),
  )).thenAnswer((Invocation invocation) {
    // 从调用参数中取出业务代码传入的verificationFailed回调
    final verificationFailedCallback = 
        invocation.namedArguments[#verificationFailed] as Function(FirebaseAuthException);
    // 构造测试用的异常,手动触发回调
    final testException = FirebaseAuthException(
      code: 'invalid-phone-number',
      message: '测试用非法手机号异常'
    );
    verificationFailedCallback(testException);
    // 返回完成的Future,匹配原方法的返回类型
    return Future.value();
  });

  // 执行被测的业务方法
  await authCodeViewModel.getSmsCodeWithFirebase();

  // 断言预期结果
  expect(authCodeViewModel.initModelState, DataState.error);
  expect(authCodeViewModel.errorMessage, 'Error code: invalid-phone-number');
});

2. 覆盖verificationCompleted(自动验证完成)分支

test('自动验证完成时正确触发登录流程', () async {
  // 构造测试用的认证凭证
  const testCredential = PhoneAuthCredential(
    verificationId: 'test_verify_id',
    smsCode: '123456',
  );

  when(mockFirebaseAuth.verifyPhoneNumber(
    phoneNumber: anyNamed('phoneNumber'),
    timeout: anyNamed('timeout'),
    codeSent: anyNamed('codeSent'),
    verificationCompleted: anyNamed('verificationCompleted'),
    verificationFailed: anyNamed('verificationFailed'),
    codeAutoRetrievalTimeout: anyNamed('codeAutoRetrievalTimeout'),
  )).thenAnswer((Invocation invocation) {
    // 取出业务代码传入的verificationCompleted回调并手动触发
    final verificationCompletedCallback = 
        invocation.namedArguments[#verificationCompleted] as Function(PhoneAuthCredential);
    verificationCompletedCallback(testCredential);
    return Future.value();
  });

  await authCodeViewModel.getSmsCodeWithFirebase();

  // 按照业务逻辑断言:比如验证是否触发了签名方法、状态是否正确流转
  verify(mockFirebaseAuth.signInWithCredential(testCredential)).called(1);

业务代码修正提示:你当前提供的业务逻辑存在缩进导致的逻辑错位——initModelState = DataState.error 目前位于verificationFailed回调的闭包外部,无论验证流程走成功还是失败分支,这行代码都会被执行,建议将这行代码挪回verificationFailed的闭包内部,否则会导致分支测试的断言结果失真。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 02:27:03