Flutter测试中非空变量mockLoginRespository未初始化报错及正确初始化问询
问题
我采用Clean TDD架构开发应用,编写登录功能测试用例时遇到以下错误:
The non-nullable local variable 'mockLoginRespository' must be assigned before it can be used. Try giving it an initializer expression, or ensure that it's assigned on every execution path.
原本在setUp函数中初始化mockLoginRespository和usecase,但仍触发该错误;将初始化代码移到test异步函数内后错误消失,但不确定这是否是正确实现方式,想咨询mockLoginRepository的正确初始化方法。
原测试代码
import 'package:dartz/dartz.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:test_app/features/login/domain/entities/user.dart'; import 'package:test_app/features/login/domain/repositories/login_repository.dart'; import 'package:test_app/features/login/domain/usecases/call_login.dart'; class MockLoginRespository extends Mock implements LoginRepository {} void main() { CallLogin usecase; MockLoginRespository mockLoginRespository; final tUserName = "test.user"; final tPassword = "test.password"; final tUser = User( userID: "test.userID", email: "test.email", userName: "test.userName"); setUp(() { mockLoginRespository = MockLoginRespository(); usecase = CallLogin(mockLoginRespository); }); test( 'should perform login from the repository', () async { when(mockLoginRespository.login(any, any)) .thenAnswer((_) async => Right(tUser)); final result = await usecase.execute(username: tUserName, password: tPassword); expect(result, Right(tUser)); verify(mockLoginRespository.login(tUserName, tPassword)); verifyNoMoreInteractions(mockLoginRespository); }, ); }
修改后的测试代码片段
test( 'should perform login from the repository', () async { // Added initialization down here mockLoginRespository = MockLoginRespository(); usecase = CallLogin(mockLoginRespository); when(mockLoginRespository.login(any, any)) .thenAnswer((_) async => Right(tUser)); // ...其余代码 }, );
解决方案
错误原因
这是Dart非空安全机制导致的:你声明的mockLoginRespository和usecase是非空类型,但静态编译器无法确认setUp函数一定会在测试执行前被调用(尽管在flutter_test的运行逻辑里setUp确实会先执行),因此判定变量可能未初始化就被使用。
正确初始化方式
推荐用以下两种方法解决,比把初始化移到test里更符合TDD测试规范:
1. 使用late修饰符
给非空变量加上late关键字,告诉编译器该变量会在首次使用前完成初始化:
void main() { late CallLogin usecase; late MockLoginRespository mockLoginRespository; // ...其余常量定义 setUp(() { mockLoginRespository = MockLoginRespository(); usecase = CallLogin(mockLoginRespository); }); // ...测试代码 }
2. 声明为可空类型并添加断言
将变量声明为可空类型,在测试开始前断言变量已初始化,确保运行时不会出现空值:
void main() { CallLogin? usecase; MockLoginRespository? mockLoginRespository; // ...其余常量定义 setUp(() { mockLoginRespository = MockLoginRespository(); usecase = CallLogin(mockLoginRespository!); }); test( 'should perform login from the repository', () async { assert(mockLoginRespository != null && usecase != null); when(mockLoginRespository!.login(any, any)) .thenAnswer((_) async => Right(tUser)); final result = await usecase!.execute(username: tUserName, password: tPassword); // ...其余断言逻辑 }, ); }
修正后的完整测试代码(推荐第一种方式)
import 'package:dartz/dartz.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:test_app/features/login/domain/entities/user.dart'; import 'package:test_app/features/login/domain/repositories/login_repository.dart'; import 'package:test_app/features/login/domain/usecases/call_login.dart'; class MockLoginRespository extends Mock implements LoginRepository {} void main() { late CallLogin usecase; late MockLoginRespository mockLoginRespository; final tUserName = "test.user"; final tPassword = "test.password"; final tUser = User( userID: "test.userID", email: "test.email", userName: "test.userName"); setUp(() { mockLoginRespository = MockLoginRespository(); usecase = CallLogin(mockLoginRespository); }); test( 'should perform login from the repository', () async { when(mockLoginRespository.login(any, any)) .thenAnswer((_) async => Right(tUser)); final result = await usecase.execute(username: tUserName, password: tPassword); expect(result, Right(tUser)); verify(mockLoginRespository.login(tUserName, tPassword)); verifyNoMoreInteractions(mockLoginRespository); }, ); }
为什么不推荐把初始化移到test里?
把初始化放在setUp中可以:
- 统一初始化逻辑,多个测试用例时无需重复编写初始化代码
- 保证每个测试用例都使用全新的mock实例,避免测试间的状态污染
内容的提问来源于stack exchange,提问作者Allen P Biju
相关产品推荐
相关产品推荐

