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类:
- 创建
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 {}
- 运行生成命令:
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
相关产品推荐
相关产品推荐

