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
相关产品推荐
相关产品推荐

