Flutter中Mock Repository返回值时出现Null类型不匹配错误
问题:Flutter TDD测试中Mockito Mock Repository返回Null类型不匹配错误
背景
我跟着Resocoder的Flutter TDD与Clean Architecture教程学习,目前仅定义了Repository抽象类,尚未实现具体类,用Mockito做Mock测试。按照依赖倒置原则,将Repository注入用例类,用例方法调用Repository对应方法并返回结果。
相关代码
Repository抽象类
abstract class NumberTriviaRepository { // 定义契约,数据层的Repository实现必须遵循这些方法 Future<Either<Failure, NumberTrivia>> getConcreteNumberTrivia(int number); Future<Either<Failure, NumberTrivia>> getRandomNumberTriva(); }
用例类
class GetConcreteNumberTrivia { final NumberTriviaRepository repository; const GetConcreteNumberTrivia({required this.repository}) : super(); Future<Either<Failure, NumberTrivia>> execute({required int number}) async { return await repository.getConcreteNumberTrivia(number); } }
Mock测试步骤
- 创建实现Repository抽象类的Mock类
- 实例化用例类并注入Mock Repository
- 创建Entity实例,通过
when和thenAnswer设置Mock Repository方法的返回值 - 调用用例方法
- 比较Mock Entity与用例返回结果
测试代码
// 创建实现Repository的Mock类用于测试 class MockNumberTriviaRepository extends Mock implements NumberTriviaRepository {} void main() { late MockNumberTriviaRepository mockNumberTriviaRepository; late GetConcreteNumberTrivia usecase; setUp(() { mockNumberTriviaRepository = MockNumberTriviaRepository(); usecase = GetConcreteNumberTrivia(repository: mockNumberTriviaRepository); }); const tNumber = 1; const tNumberTrivia = NumberTrivia(text: 'test', number: tNumber); test('should get trivia for the number form the repository', () async { // arrange when( mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber)) .thenAnswer((_) async => const Right(tNumberTrivia)); // act final result = await usecase.execute(number: tNumber); // assert expect(result, const Right(tNumberTrivia)); verify(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber)); verifyNoMoreInteractions(mockNumberTriviaRepository); }); }
错误信息
type 'Null' is not a subtype of type 'Future<Either<Failure, NumberTrivia>>' test/features/number_trivia/domain/usecases/get_concrete_number_trivia_test.dart 11:7 MockNumberTriviaRepository.getConcreteNumberTrivia test/features/number_trivia/domain/usecases/get_concrete_number_trivia_test.dart 38:40 main.<fn>
我注释掉断言和用例调用部分仍报错,怀疑是thenAnswer的返回值设置有问题,为什么Mock Entity没被正确返回?
问题原因与解决方案
1. 异步Mock的类型推断问题
Dart的类型推断在异步Mock场景下偶尔会失效,你可以把async =>替换成Future.value()显式返回Future对象,避免类型推断错误:
when(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber)) .thenAnswer((_) => Future.value(const Right(tNumberTrivia)));
2. Mock类的生成方式问题
手动编写的Mock类在Mockito新版本中可能存在兼容性问题,建议用build_runner自动生成Mock类:
- 在测试文件开头添加注解:
import 'package:mockito/mockito.dart'; import 'package:test/test.dart'; // 其他必要导入 @GenerateMocks([NumberTriviaRepository]) void main() { late MockNumberTriviaRepository mockNumberTriviaRepository; // ... 剩余测试代码 }
- 运行生成命令:
flutter pub run build_runner build
替换掉手动编写的MockNumberTriviaRepository类,用生成的版本。
3. Mockito版本写法差异
如果你使用的是Mockito 5.x及以上版本,when的参数匹配需要更严谨,显式指定Invocation参数也能解决问题:
when(mockNumberTriviaRepository.getConcreteNumberTrivia(tNumber)) .thenAnswer((Invocation invocation) async => const Right(tNumberTrivia));
4. 隐藏的拼写错误
注意到Repository抽象类里的方法名getRandomNumberTriva少了一个'l'(正确应为getRandomNumberTrivia),虽然不影响当前测试,但后续测试该方法会出问题,建议提前修正。
5. 依赖版本兼容性检查
确保mockito和test包版本兼容,在pubspec.yaml中使用稳定版本:
dev_dependencies: flutter_test: sdk: flutter mockito: ^5.4.0 build_runner: ^2.4.4
内容的提问来源于stack exchange,提问作者Vincenzo
相关产品推荐
相关产品推荐

