You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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类:

  1. 在测试文件开头添加注解:
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
// 其他必要导入

@GenerateMocks([NumberTriviaRepository])
void main() {
  late MockNumberTriviaRepository mockNumberTriviaRepository;
  // ... 剩余测试代码
}
  1. 运行生成命令:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 11:13:19