mcpField
T
testing

testing

github.com/evanca测试与质量665227 次安装1 天前更新
代码仓库

关于 testing

为 Flutter 和 Dart 开发者提供编写、评审测试的指南,强调测试应能发现真实回归,并涵盖单元测试、Widget 测试及测试组织。

主要功能

  1. 01检查测试能否发现真实问题
  2. 02按类组织测试并命名用例
  3. 03区分单元测试与 Widget 测试
  4. 04编写 Widget 测试
  5. 05遵循 mocktail 模拟规范
  6. 06镜像源代码目录组织测试文件

适用场景

  1. 01为业务逻辑和仓库编写单元测试
  2. 02验证 Widget 渲染与交互
  3. 03审查测试的有效性与覆盖
  4. 04排查不稳定或误报的测试

安装

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

$npx skills add https://github.com/evanca/flutter-ai-rules --skill testing

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

详细介绍

测试技能

编写有效、有意义的 Flutter 和 Dart 测试,捕获真正的回归问题。

适用场景

在以下情况下使用此技能:

  • 为业务逻辑、仓库或实用工具类编写单元测试。
  • 为 UI 组件编写组件测试。
  • 检查现有测试的正确性和覆盖率。
  • 修复不稳定或误报的测试。
  • 决定使用单元测试、组件测试还是集成测试。

1. 测试有效性

编写或接受测试之前,先问:

“如果真实代码损坏,这个测试真的会失败吗?”

  • 避免只验证模拟或伪造行为、却未执行真实逻辑的测试。
  • 避免验证语言或标准库保证的行为。
  • 每项测试都必须能够捕获真正的回归问题。
// BAD — tests the mock, not real logic
test('should return user', () {
  when(() => repo.getUser()).thenReturn(fakeUser);
  expect(repo.getUser(), fakeUser); // Only proves the mock works
});

// GOOD — tests the cubit's state transitions driven by the mock
blocTest<UserCubit, UserState>(
  'should emit loaded state when getUser succeeds',
  build: () {
    when(() => repo.getUser()).thenAnswer((_) async => fakeUser);
    return UserCubit(repo);
  },
  act: (cubit) => cubit.fetchUser(),
  expect: () => [
    const UserState(status: UserStatus.loading),
    UserState(status: UserStatus.loaded, user: fakeUser),
  ],
);

2. 结构

测试文件中始终使用 group()。组名应为被测类的名称:

group('Counter', () {
  late Counter counter;

  setUp(() {
    counter = Counter();
  });

  test('value should start at 0', () {
    expect(counter.value, 0);
  });

  test('should increment value by 1', () {
    counter.increment();
    expect(counter.value, 1);
  });
});

规则:

  • 使用 setUp 创建共享对象;使用 tearDown 进行清理(关闭流、控制器)。
  • 每项测试只关注一种行为。
  • 如果一个类有许多方法,请为子功能嵌套 group() 块。

3. 命名

使用 “should” 描述预期行为,为测试用例命名:

test('should emit updated list when item is added', () { ... });
test('should throw ArgumentError when input is negative', () { ... });

4. 单元测试与组件测试

类型测试目标工具
单元测试纯 Dart 逻辑、仓库、cubit/bloctest、bloc_test、mocktail
组件测试单个组件、UI 行为、导航flutter_test、WidgetTester

业务逻辑默认使用单元测试。验证 UI 渲染、手势处理或组件交互时,使用组件测试。


5. 组件测试模式

testWidgets('should display error message on failure', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      home: BlocProvider<LoginCubit>.value(
        value: mockLoginCubit,
        child: const LoginView(),
      ),
    ),
  );

  // Simulate failure state
  whenListen(
    mockLoginCubit,
    Stream.fromIterable([const LoginState(status: LoginStatus.failure, errorMessage: 'Invalid')]),
    initialState: const LoginState(),
  );
  await tester.pump();

  expect(find.text('Invalid'), findsOneWidget);
});

规则:

  • 将组件包裹在 MaterialApp(或应用的根组件)中,以提供 MediaQuery、Directionality 等内容。
  • 单帧使用 pump();需要动画播放完毕时使用 pumpAndSettle()。
  • 对可能包含本地化或动态文本的组件,优先使用 find.byKey,而不是 find.text。

6. 模拟最佳实践

  • 使用 mocktail 创建模拟对象(无需代码生成)。
  • 对传递给 any() 的自定义类型,在 setUpAll 中调用 registerFallbackValue()。
  • 在仓库边界进行模拟,而不是在 HTTP/数据库层进行模拟。
class MockAuthRepository extends Mock implements AuthRepository {}

void main() {
  setUpAll(() {
    registerFallbackValue(FakeLoginRequest());
  });

  // ... tests
}

7. 测试文件组织

test/
  feature_a/
    cubit/
      feature_a_cubit_test.dart
    view/
      feature_a_view_test.dart
    model/
      feature_a_model_test.dart
  • 在 test/ 下镜像 lib/ 文件夹结构。
  • 每个源文件对应一个测试文件。
  • 将测试文件命名为 <source_file>_test.dart。

评论

支持 Markdown