Flutter测试报错:预期抛出UserNotFoundAuthException却返回Future<AuthUser>
Flutter测试用例失败问题修复
问题核心原因
- 异步异常断言方式错误:直接对
Future对象使用expect,不会等待Future执行完成,因此无法捕获异步抛出的异常,测试会认为返回的是Future<AuthUser>实例而非预期异常。 - 测试未初始化Provider:
createUser方法要求Provider必须初始化,否则会抛出NotInitializedException,而非测试预期的用户相关异常。 - 异步方法未等待执行:
sendVerificationEmail是异步方法,直接调用不等待会导致currentUser未及时更新,断言失败。 - 测试数据不匹配:原测试中触发异常的邮箱/密码与
logIn方法内的判断条件不符,导致无法抛出预期异常。
修复后的完整代码
import 'package:belunotes/services/auth/auth_exceptions.dart'; import 'package:belunotes/services/auth/auth_provider.dart'; import 'package:belunotes/services/auth/auth_user.dart'; import 'package:test/test.dart'; void main() { group('Mock Authentication', () { late MockAuthProvider provider; // 每个测试前初始化Provider,保证测试独立性 setUp(() async { provider = MockAuthProvider(); await provider.initialize(); }); test('Should not be initialized before setUp', () { final freshProvider = MockAuthProvider(); expect(freshProvider.isInitialized, false); }); test('Cannot log out if not initialized', () { final freshProvider = MockAuthProvider(); expect( freshProvider.logOut(), throwsA(const TypeMatcher<NotInitializedException>()), ); }); test('Should be able to be initialized', () { expect(provider.isInitialized, true); }); test('User should be null after initialization', () { expect(provider.currentUser, null); }); test( 'Should be able to initialize in less than 2 seconds', () async { final freshProvider = MockAuthProvider(); await freshProvider.initialize(); expect(freshProvider.isInitialized, true); }, timeout: const Timeout(Duration(seconds: 2)), ); test('Create user should delegate to logIn function', () async { // 使用expectLater等待异步执行,捕获异常 await expectLater( provider.createUser(email: 'ayushraj1268@gmail.com', password: 'anypassword'), throwsA(const TypeMatcher<UserNotFoundAuthException>()), ); await expectLater( provider.createUser(email: 'someone@bar.com', password: 'Ayush1268'), throwsA(const TypeMatcher<WrongPasswordFoundAuthException>()), ); final user = await provider.createUser( email: 'foo', password: 'bar', ); expect(provider.currentUser, user); expect(user.isEmailVerified, false); }); test('Logged in user should be able to get verified', () async { // 先登录获取有效用户 await provider.createUser(email: 'foo', password: 'bar'); // 等待异步验证邮件方法执行完成 await provider.sendVerificationEmail(); final user = provider.currentUser; expect(user, isNotNull); expect(user!.isEmailVerified, true); }); test('Should be able to log out and log in again', () async { await provider.createUser(email: 'foo', password: 'bar'); await provider.logOut(); expect(provider.currentUser, null); await provider.logIn( email: 'email', password: 'password', ); final user = provider.currentUser; expect(user, isNotNull); }); }); } class NotInitializedException implements Exception {} class MockAuthProvider implements AuthProvider { AuthUser? _user; var _isInitialized = false; bool get isInitialized => _isInitialized; @override Future<AuthUser> createUser({ required String email, required String password, }) async { if (!isInitialized) throw NotInitializedException(); await Future.delayed(const Duration(seconds: 1)); return logIn( email: email, password: password, ); } @override AuthUser? get currentUser => _user; @override Future<void> initialize() async { await Future.delayed(const Duration(seconds: 1)); _isInitialized = true; } @override Future<AuthUser> logIn({required String email, required String password}) { if (!isInitialized) throw NotInitializedException(); if (email == 'ayushraj1268@gmail.com') throw UserNotFoundAuthException(); if (password == 'Ayush1268') throw WrongPasswordFoundAuthException(); const user = AuthUser(isEmailVerified: false); _user = user; return Future.value(user); } @override Future<void> logOut() async { if (!isInitialized) throw NotInitializedException(); if (_user == null) throw UserNotFoundAuthException(); await Future.delayed(const Duration(seconds: 1)); _user = null; } @override Future<void> sendVerificationEmail() async { if (!isInitialized) throw NotInitializedException(); final user = _user; if (user == null) throw UserNotFoundAuthException(); const newUser = AuthUser(isEmailVerified: true); _user = newUser; } }
关键修复点说明
- 添加
setUp初始化逻辑:每个测试前创建新的Provider实例并完成初始化,避免测试间状态污染,同时确保后续测试能正常调用需要初始化的接口。 - 异步异常断言改为
expectLater:expectLater会等待Future执行完成,正确捕获异步抛出的异常,解决"返回Future实例而非异常"的问题。 - 修正测试匹配数据:调整触发异常的邮箱/密码,与
logIn方法内的判断条件保持一致,确保抛出预期的异常类型。 - 等待异步方法执行:对
sendVerificationEmail、logOut等异步方法添加await,确保状态更新完成后再执行断言。 - 优化测试独立性:将"未初始化状态"相关测试改为使用新创建的Provider实例,避免与其他测试的初始化状态冲突。
内容的提问来源于stack exchange,提问作者Ayush Raj
相关产品推荐
相关产品推荐

