Skip to content

测试工件 advanced 4.0.11+ experimental

WARNING

这是一个高级 API。作为用户,你很可能想要使用 测试注解 来为你的测试添加笔记或上下文。这主要用于内部和库作者。

测试工件允许在测试执行期间附加或记录结构化数据、文件或元数据。这是一个低级功能,主要设计用于:

  • 内部使用(annotate 是建立在工件系统之上的)
  • 框架作者在 Vitest 之上创建自定义测试工具

每个工件包括:

  • 一个类型区分符,它是工件类型的唯一标识符
  • 自定义数据,可以是任何相关信息
  • 可选附件,要么是文件要么是与工件关联的内联内容
  • 一个源代码位置,指示工件创建的位置

Vitest 自动管理附件序列化(文件被复制到 attachmentsDir)并注入源代码位置元数据,所以你可以专注于想要记录的数据。所有工件 必须 继承自 TestArtifactBase 并且所有附件继承自 TestAttachment 才能在内部被正确处理。

API

recordArtifact experimental

WARNING

recordArtifact 是一个实验性 API。破坏性变更可能不遵循 SemVer,请在使用时锁定 Vitest 的版本。

API 表面可能会根据反馈而变化。我们鼓励你尝试并与团队分享你的经验。

ts
function recordArtifact<Artifact extends TestArtifact>(task: Test, artifact: Artifact): Promise<Artifact>

recordArtifact 函数在测试执行期间记录工件并返回它。它期望第一个参数是一个 task,第二个参数是一个可赋值给 TestArtifact 的对象。

INFO

工件必须在任务被报告之前记录。之后记录的任何工件将不会包含在任务中。

当在测试上记录工件时,它会发出 onTestArtifactRecord 运行器事件和 onTestCaseArtifactRecord 报告器事件。要从测试用例中检索记录的工件,请使用 artifacts() 方法。

注意:注解,即使它们是建立在此功能之上的,为了向后兼容原因,直到下一个主要版本之前,不会出现在 task.artifacts 数组中。

TestArtifact

TestArtifact 类型是一个联合类型,包含 Vitest 可以产生的所有工件,包括自定义的。所有工件都扩展自 TestArtifactBase

TestArtifactBase experimental

ts
export interface TestArtifactBase {
  /** 与此工件关联的文件或数据附件 */
  attachments?: TestAttachment[]
  /** 创建此工件的源代码位置 */
  location?: TestArtifactLocation
}

TestArtifactBase 接口是所有测试工件的基础。

创建自定义测试工件时扩展此接口。Vitest 自动管理 attachments 数组并注入 location 属性以指示工件是在测试代码中的何处创建的。

DANGER

当在 api.allowWritebrowser.api.allowWrite 禁用的情况下运行时,Vitest 会在报告之前清空每个工件上的 attachments 数组。

如果你的自定义工件缩小了 attachments 类型(例如变为元组),请在联合类型中包含 | [],以便类型反映运行时实际发生的情况。

TestAttachment

ts
export interface TestAttachment {
  /** 附件的 MIME 类型(例如,'image/png', 'text/plain') */
  contentType?: string
  /** 附件的文件系统路径 */
  path?: string
  /** 内联附件内容,作为字符串或原始二进制数据 */
  body?: string | Uint8Array
  /**
   * @experimental
   * 字符串 `body` 是如何编码的。
   * - `'base64'` (默认): body 已经是 base64 编码
   * - `'utf-8'`: body 是一个 utf8 字符串
   */
  bodyEncoding?: 'base64' | 'utf-8'
}

TestAttachment 接口表示与测试工件关联的文件或数据附件。

附件可以是基于文件的(通过 path)或内联内容(通过 body)。contentType 帮助使用者理解如何解释附件数据。

如果你传递一个字符串 body,Vitest 假设它已经是 base64 编码的,除非你设置 bodyEncoding: 'utf-8'。当你传递 bodyUint8Array 时,Vitest 自动将其编码为 base64。bodyEncoding 选项仅适用于内联 body 附件,不适用于 path 附件。

TestArtifactLocation

ts
export interface TestArtifactLocation {
  /** 源文件中的行号(从 1 开始索引) */
  line: number
  /** 行中的列号(从 1 开始索引) */
  column: number
  /** 源文件的路径 */
  file: string
}

TestArtifactLocation 接口表示测试工件的源代码位置信息。它指示工件源自源代码中的何处。

TestArtifactRegistry

TestArtifactRegistry 接口是自定义测试工件类型的注册表。

使用 TypeScript 的模块增强功能 增强此接口允许注册测试可以产生的自定义工件类型。

每个自定义工件应该扩展 TestArtifactBase 并包含一个唯一的 type 区分符属性。

以下是一些要遵循的指南或最佳实践:

  • 尝试使用 Symbol 作为 注册表键 以保证唯一性
  • type 属性应遵循模式 'package-name:artifact-name''internal:' 是保留前缀
  • 使用 attachments 包含文件或数据;扩展 TestAttachment 以获取自定义元数据
  • 如果你缩小了 attachments 类型(例如变为元组),在联合类型中包含 | [],因为 Vitest 可能在运行时清空数组(见 TestArtifactBase
  • location 属性是自动注入的

自定义工件

要以类型安全的方式使用和管理工件,你需要创建其类型并注册它:

ts
import type { TestArtifactBase, TestAttachment } from 'vitest'

interface A11yReportAttachment extends TestAttachment {
  contentType: 'text/html'
  path: string
}

interface AccessibilityArtifact extends TestArtifactBase {
  type: 'a11y:report'
  passed: boolean
  wcagLevel: 'A' | 'AA' | 'AAA'
  attachments: [A11yReportAttachment] | []
}

const a11yReportKey = Symbol('report')

declare module 'vitest' {
  interface TestArtifactRegistry {
    [a11yReportKey]: AccessibilityArtifact
  }
}

只要类型可分配给它们的基础且没有错误,一切应该正常工作,你应该能够使用 recordArtifact 记录工件:

ts
async function toBeAccessible(
  this: MatcherState,
  actual: Element,
  wcagLevel: 'A' | 'AA' | 'AAA' = 'AA'
): AsyncExpectationResult {
  const report = await runAccessibilityAudit(actual, wcagLevel)

  await recordArtifact(this.task, {
    type: 'a11y:report',
    passed: report.violations.length === 0,
    wcagLevel,
    attachments: [{
      contentType: 'text/html',
      path: report.path,
    }],
  })

  return {
    pass: violations.length === 0,
    message: () => `Found ${report.violations.length} accessibility violation(s)`
  }
}

与注解的关系

测试注解是建立在工件系统之上的。当在测试中使用注解时,它们在底层创建 internal:annotation 工件。然而,注解是:

  • 更易于使用
  • 为最终用户设计,而不是开发者

如果你只是想给测试添加笔记,请使用注解。如果你需要自定义数据,请使用工件。