mcpField
D
dart-test-fundamentals

dart-test-fundamentals

github.com/kevmoo测试与质量148450 次安装2 周前更新
代码仓库

关于 dart-test-fundamentals

dart-test-fundamentals 是面向 Dart 开发者的测试组织与配置技能,涵盖测试结构、生命周期和清理,并说明何时不应套用相关规范。

主要功能

  1. 01使用 test 和 group 组织测试
  2. 02应用 setup 与 teardown 生命周期方法
  3. 03配置平台、标签和超时
  4. 04采用资源清理与测试替身
  5. 05遵循断言和测试接缝指南

适用场景

  1. 01编写或整理 Dart 测试套件
  2. 02在 dart_test.yaml 中配置测试执行
  3. 03选择测试资源的清理方式

安装

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

$npx skills add https://github.com/kevmoo/dash_skills --skill dart-test-fundamentals

安装后在 Claude Code 中输入/dart-test-fundamentals确认已加载。

附带文件evals/evals.json

详细介绍

Dart 测试基础

何时使用此技能

适用于以下情况:

  • 编写新的测试文件。
  • 使用 group 组织测试套件。
  • 通过 dart_test.yaml 配置测试执行。
  • 了解测试生命周期方法。
何时不使用(弃用防护规则)

遇到以下情况时,请勿使用此技能,也不要重构现有测试:

  • 旧式单分组变更:除非明确要求,否则不要删除或重新格式化未修改的现有测试中的 group 层级,因为这会造成不必要的差异变更。
  • 其他断言框架:如果软件包已迁移到 package:checks 或专用测试框架,请勿将测试改回旧式 package:matcher 用法。
  • 无需设置的简单测试:没有共享状态或资源的简单独立测试不需要 group、setUp 或 addTearDown。不要添加形式主义的包装样板代码。

发现

查找可改进测试结构的候选项:

try-finally 清理

搜索使用 try-finally 清理,而非 addTearDown 的测试:

  • 正则表达式:\bfinally\s*\{(检查它是否用于测试中的资源清理)。

核心概念

1. 测试结构(test 和 group)
  • test:测试的基本单元。

    test('description', () {
      // assertions
    });
    
  • group:用于将测试组织成逻辑区块。

    • 可以嵌套分组。
    • 描述会串联起来(例如,“分组描述 测试描述”)。
    • 有助于限定 setUp 和 tearDown 调用的作用范围。
    • 命名:对于对应类名的分组,请使用 PascalCase(例如,group('MyClient', ...))。
    • 避免单一分组:如果一个文件中只有一个 group 调用,请勿将所有测试都包装在该分组中。
      • 注意:清理你没有创建的现有代码时,除非明确要求,否则不要删除分组。这可能会造成大量差异,令大多数工程师不愿接受!
  • 命名测试 test('此处填写测试名称',:

    • 避免添加多余的“test”前缀。请改用 group。
    • 在描述中写明预期行为或结果(例如,'抛出 StateError' 或 '将 API 密钥添加到 URL')。
    • 描述与分组名称串联后应通顺易读。
  • 命名参数的位置:

    • 对于 test 和 group 调用,应将命名参数(例如,testOn、timeout、skip)放在描述字符串之后、回调闭包之前。这样可将测试逻辑放在最后,提高可读性。
      test('描述', testOn: 'vm', () {
        // assertions
      });
      
2. 生命周期方法(setUp、tearDown)
  • setUp:在当前 group(及其嵌套分组)中的每个 test 之前运行。
  • tearDown:在当前 group 中的每个 test 之后运行。
  • setUpAll:在该分组中的任何测试开始之前运行一次。
  • tearDownAll:在该分组中的所有测试结束后运行一次。

最佳实践:

  • 使用 setUp 重置状态,以确保测试隔离。
  • 避免在测试之间共享可变状态,除非每次都会重置。
3. 清理资源
  • 清理在 test 函数体内创建的资源时,可以考虑使用 addTearDown,而非 try-finally 代码块。

避免:

test('can create and delete a file', () {
  final file = File('temp.txt');
  try {
    file.writeAsStringSync('hello');
    expect(file.readAsStringSync(), 'hello');
  } finally {
    if (file.existsSync()) file.deleteSync();
  }
});

推荐:

test('can create and delete a file', () {
  final file = File('temp.txt');
  // Register teardown immediately after resource creation intent
  addTearDown(() {
    if (file.existsSync()) file.deleteSync();
  });

  file.writeAsStringSync('hello');
  expect(file.readAsStringSync(), 'hello');
});
4. 配置(dart_test.yaml)

dart_test.yaml 文件用于配置测试运行器。常见配置包括:

平台

定义测试的运行平台(vm、chrome、node)。

platforms:
  - vm
  - chrome
标签

对测试进行分类,以便运行特定子集。

tags:
  integration:
    timeout: 2x

代码中的用法:

@Tags(['integration'])
import 'package:test/test.dart';

运行带标签的测试:dart test --tags integration

超时

设置测试的默认超时时间。

timeouts:
  2x # Double the default timeout
5. 文件命名
  • 测试文件必须以 _test.dart 结尾,才能被测试运行器识别。
  • 将测试放在 test/ 目录中。
6. 测试设计、接缝与真实测试替身
  • 测试接缝规范(包集成测试与 lib/src/ 单元测试):
    • 包级测试与集成测试:导入 package:<pkg>/<pkg>.dart (公共入口接缝),并严格限定 lib/<pkg>.dart 导出的范围,仅供 公共使用者使用。
    • 子系统单元测试(lib/src/):导入内部的深层模块 (package:<pkg>/src/<subsystem>.dart)——例如未导出的解析器、状态 机、数据模型或接口简单、内部行为丰富的算法——以便直接对边界情况进行 单元测试;通过所属模块的入口测试仅有一个调用方的轻量辅助函数。
  • 真实实现与一方伪件(“可能失败的测试”):使用真实依赖和一方伪件进行测试,确保生产契约遭到破坏时测试会失败——文件系统 I/O 使用 package:test_descriptor(d.sandbox、d.dir、d.file)或 Directory.systemTemp.createTempSync()(始终使用 addTearDown(() => tempDir.deleteSync(recursive: true)) 注册清理);服务使用内存数据库或环回 HttpServer 实例;HTTP 使用 package:http/testing.dart(MockClient);自定义接口使用手写伪件/存根;DOM 和 JS/Wasm 互操作使用 @TestOn('browser')。
  • 确定性的异步处理与匹配器诊断(fakeAsync 与 package:checks):避免在单元测试中使用真实的 await Future.delayed(...) 延时(这会导致测试不稳定并拖慢测试套件);优先使用 fakeAsync (package:fake_async)、可控的 Completer,或注入时钟/运行器回调。使用 emitsInOrder/expectLater 验证 Stream 预期,而不要使用不传播错误的未等待 stream.listen(expectAsync1(...));优先使用具体的匹配器 (throwsA(isA<StateError>().having((e) => e.message, 'message', contains('...'))), completion(...)、unorderedEquals(...)),而不是 expect(x == y, isTrue) 或 手动编写 try { ...; fail('...'); } catch (e) {} 代码块。
  • 行为、结构和关系断言(不编写变更检测测试):
    • 使用合成输入测试使用方行为:使用合成的单元测试输入,断言会_使用_常量、配置和模型的代码的可观察输出、状态转换和边界条件(例如,向验证器传入 280 个字符与 281 个字符,或向仓库分类器传入合成临时目录),而不是编写同义反复的变更检测测试,重复常量字面值、固定静态配置集中的项目成员关系(publishedPackages.contains('foo'))、硬编码具名生产实体(classifyRepo('specific-repo')),或在单个静态模板常量中用子串匹配字面操作名称、CLI 标志或固定的工具版本/SHA。更新配置集、模板常量或数据集导致现有变更检测断言失败时,删除该变更检测断言,而不是修改其中的字面值以匹配。
    • 对实时数据和模板断言结构与关系不变量:测试会独立于逻辑演进的实时目录、JSON 数据集、注册表或规范模板时,断言结构有效性和跨构件关系不变量(例如,isNotEmpty、存在必需的架构键、mergedCount + openCount == totalCount,或工作流模板作业 ID 与分支保护检查名称一致),而不是硬编码条目总数(hasLength(33))、字面配置项或单个常量模板子串(确定性单元测试输入除外,可断言精确的数量/值相等)。
  • 直接执行与渲染验证:通过调用函数、运行 CLI 命令或直接渲染组件,验证运行时行为、控制流和 UI/CLI 输出。仅在检查静态文档漂移(README.md 中的 --help 代码块)、构建/包元数据同步(BUILD、pubspec.yaml)以及代码生成器输入/输出测试夹具时,使用原始文件文本读取(readAsStringSync())。

常用命令

  • dart test:运行所有测试。
  • dart test test/path/to/file_test.dart:运行指定文件。
  • dart test --name "substring":运行描述匹配的测试。

相关技能

dart-test-fundamentals 是组织和配置测试的核心技能。要在这些测试中编写断言,请参阅:

评论

支持 Markdown