mcpField
F
flutter-tester

flutter-tester

github.com/harishwarrior测试与质量66423 次安装15 周前更新
代码仓库

关于 flutter-tester

为 Flutter 项目提供分层测试指导的技能,适用于编写仓库、DAO、服务、Provider 和 Widget 测试。内容涵盖 Given-When-Then 组织方式、依赖模拟、测试运行及常见错误。

主要功能

  1. 01按架构层隔离测试
  2. 02采用 Given-When-Then 结构
  3. 03提供 Riverpod 与 Widget 测试指南
  4. 04指导 Mock、Fake 与依赖覆盖
  5. 05包含测试运行命令与检查清单

适用场景

  1. 01为 Repository 和 DAO 编写测试
  2. 02验证 Riverpod 状态与生命周期
  3. 03测试 Widget 交互和导航
  4. 04检查异步加载、错误及边界状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/harishwarrior/flutter-claude-skills --skill flutter-tester

安装后在 Claude Code 中输入/flutter-tester确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
layer_testing_patterns.md
riverpod_testing_guide.md
widget_testing_guide.md

详细介绍

Flutter Tester

要求

  • Flutter 项目依赖 flutter_test
  • 支持 Riverpod、Mockito 和 GetIt
  • 添加 @GenerateMocks 注解后,运行 dart run build_runner build 生成 mock
  • 兼容 FVM(使用 fvm flutter test 而不是 flutter test)

概览

使用 Given-When-Then 结构,分别测试各个架构层。始终测试成功和错误路径。不要 mock provider,而应覆盖它们的依赖项。

参考文件

根据测试内容加载相应文件:

测试内容参考文件
Repository、DAO、Service 逻辑references/layer_testing_patterns.md
Widget UI、交互、对话框、导航references/widget_testing_guide.md
Riverpod provider 状态、变更、生命周期references/riverpod_testing_guide.md

核心原则

1. 分层隔离

使用各层自己的 mock 依赖项进行测试:

层测试内容要 mock 的对象
Repository数据源之间的数据协调DAO、API、Logger
DAO数据库 CRUD 操作使用真实的内存数据库,mock Logger
Provider状态管理和转换Service、Repository
Service业务逻辑和工作流Repository、网络客户端
WidgetUI 行为和交互Provider 依赖项(通过覆盖)
2. Given-When-Then 结构
test('Given valid data, When fetchUsers called, Then returns user list', () async {
  // Arrange (Given)
  when(mockDAO.fetchAll()).thenAnswer((_) async => expectedUsers);

  // Act (When)
  final result = await repository.fetchUsers();

  // Assert (Then)
  expect(result, equals(expectedUsers));
  verify(mockDAO.fetchAll()).called(1);
});
3. 测试组织
group('UserRepository', () {
  group('fetchUsers', () {
    setUp(() { /* init mocks, register with GetIt */ });
    tearDown(() => GetIt.I.reset()); // Always reset GetIt

    test('Given success ... When ... Then ...', () { });
    test('Given error  ... When ... Then ...', () { });
  });
});

标准测试设置

生成 Mock
@GenerateMocks([IUserDAO, IUserAPI, ILogger])
void main() { ... }

修改 @GenerateMocks 后,运行 dart run build_runner build。

在 GetIt 中注册
setUp(() {
  mockDAO = MockIUserDAO();
  mockLogger = MockILogger();
  GetIt.I
    ..registerSingleton<IUserDAO>(mockDAO)
    ..registerSingleton<ILogger>(mockLogger);
});

tearDown(() => GetIt.I.reset()); // Critical — always reset
Fake 与 Mock
  • Fake(class FakeLogger extends ILogger)——静默存根;无需验证调用时使用
  • Mock(MockILogger)——需要使用 when()、verify() 或 thenThrow() 时使用

快速参考

场景关键模式
测试 RepositoryMock DAO + API → 注入 Repository 构造函数
测试 DAO在 setUp 中使用 FakeDatabase 或 openInMemoryDatabase(),在 tearDown 中删除表
测试 Riverpod providercreateContainer(overrides: [serviceProvider.overrideWith(...)])
测试 Widget设置屏幕尺寸,使用 find.byKey(),调用 pumpAndSettle()
测试加载状态使用 Completer,调用 pump() 断言加载状态,完成后再次调用 pump()
测试平台专属 UIdebugDefaultTargetPlatformOverride = TargetPlatform.iOS——之后重置
测试 GoRouter 导航FakeGoRouter + MockGoRouterProvider

运行测试

flutter test --coverage                       # All tests with coverage
flutter test test/path/to/test.dart           # Specific file
flutter test --plain-name "Given valid data"  # Filter by name
genhtml coverage/lcov.info -o coverage/html   # Generate HTML coverage report
# Prefix any command with `fvm` if using Flutter Version Manager

常见错误

错误修正方法
直接 mock provider覆盖其依赖项:provider.overrideWith(...)
在 tearDown 中漏掉 GetIt.I.reset()测试会相互影响——务必重置
在测试中使用 await Future.delayed()改用 await tester.pumpAndSettle() 或 Completer
按文本字符串查找 Widget使用 find.byKey(const Key('name'))——文本变更后仍然稳定
Widget 测试中未设置屏幕尺寸添加 tester.view.physicalSize = const Size(1000, 1000)
未重置 debugDefaultTargetPlatformOverride在测试末尾将其设为 null
tearDown() 未使用 lambda写成 tearDown(() async { ... }),而不是 tearDown() async { ... }

测试清单

设置与模拟:

  • 模拟依赖项(而非提供方)
  • 如使用 SharedPreferences,则对其进行模拟
  • 在 tearDown 中调用 GetIt.I.reset()
  • 在 tearDown 中关闭流
  • 在 tearDown 中释放控制器

Widget 测试:

  • 为源 Widget 添加键,并在 find.byKey() 中使用
  • 设置屏幕尺寸(physicalSize + devicePixelRatio)
  • 重置平台覆盖(debugDefaultTargetPlatformOverride = null)
  • 如适用,验证导航

测试覆盖率:

  • 覆盖成功和失败路径
  • 测试边缘情况(null、空值、最大值)
  • 测试加载和错误状态
  • 正确处理异步操作(不使用 Future.delayed)

代码质量:

  • 使用 Given-When-Then 命名方式
  • 在适当情况下使用 verify() 或 verifyNever()
  • 测试彼此隔离且结果确定

评论

支持 Markdown