测试简介

本指南涵盖 AdonisJS 应用中的测试配置。你将学习:

  • 关于 Japa,AdonisJS 使用的测试框架
  • 测试如何通过套件(suites)和插件进行配置
  • 如何创建并运行你的第一个测试
  • 如何按文件、名称、标签或套件筛选测试
  • 如何使用 watch 模式进行快速开发
  • 如何为测试覆盖环境变量

概述

AdonisJS 内置了对测试的支持,所有 starter kit 都预配置了完整的测试环境。你可以无需任何额外配置就立即开始编写测试。

测试层由 Japa 驱动,这是我们构建并维护超过七年的测试框架。与 Jest 或 Vitest 这类通用测试运行器不同,Japa 是专门为后端应用打造的。它在 Node.js 中原生运行,无需转译器,并且包含了专为后端测试设计的插件,例如用于测试 JSON 端点的 API 客户端,以及用于在测试期间管理临时文件的 filesystem 插件。

我们选择构建并维护自己的测试框架,是为了避免 JavaScript 生态系统中常见的频繁更迭。在见证了社区从 Mocha 转向 Jest 再转向 Vitest 之后,我们很高兴我们投资了可控、并能随 AdonisJS 一同演进的工具。

Japa 与 AdonisJS 的集成

Japa 通过 @japa/plugin-adonisjs 包与 AdonisJS 深度集成。该插件用 AdonisJS 专属的实用工具扩展了 Japa,让你的测试能够访问应用实例、用于计算 URL 的路由辅助方法,以及在 HTTP 和浏览器测试中读写 cookie 的方法。

这种集成意味着你编写的测试是 AdonisJS 原生的,而不是硬套一个不了解你应用结构的通用测试运行器。

项目结构

AdonisJS 将测试组织成套件(suites),每个套件代表一个具有自身配置的测试类别。一个典型的项目结构如下。

tests/
├── bootstrap.ts
├── unit/
   └── posts_service.spec.ts
└── browser/
    └── posts.spec.ts

tests/bootstrap.ts 文件配置 Japa 插件与生命周期钩子。各个测试文件位于套件目录下,每个套件可以有适合该测试类型的不同超时时间、插件和设置逻辑。

了解套件

测试套件将共享共同特征的测试归为一组。例如,单元测试运行迅速且不需要 HTTP 服务器,而浏览器测试需要运行中的服务器和更长的超时时间以应对浏览器自动化。

Hypermedia 和 Inertia starter kit 预配置了两个套件:

  • unit(单元测试):隔离测试服务、工具函数和模型等代码片段
  • browser(浏览器测试):使用 Playwright 进行端到端测试,模拟真实用户交互

套件在 adonisrc.ts 文件中定义。

adonisrc.ts
{
  tests: {
    suites: [
      {
        files: ['tests/unit/**/*.spec.ts'],
        name: 'unit',
        timeout: 2000,
      },
      {
        files: ['tests/browser/**/*.spec.ts'],
        name: 'browser',
        timeout: 300000,
      },
    ],
    forceExit: false,
  }
}

每个套件都指定了一个用于定位测试文件的 glob 模式、一个用于筛选的名称,以及以毫秒为单位的超时时间。浏览器测试的超时时间要长得多(5 分钟),因为浏览器自动化本质上比进程内单元测试更慢。

配置插件与钩子

tests/bootstrap.ts 文件是你配置 Japa 插件以及定义在所有测试前后运行的生命周期钩子的地方。

tests/bootstrap.ts
import { assert } from '@japa/assert'
import app from '@adonisjs/core/services/app'
import type { Config } from '@japa/runner/types'
import { pluginAdonisJS } from '@japa/plugin-adonisjs'
import testUtils from '@adonisjs/core/services/test_utils'
import { browserClient } from '@japa/browser-client'
import { authBrowserClient } from '@adonisjs/auth/plugins/browser_client'
import { sessionBrowserClient } from '@adonisjs/session/plugins/browser_client'

/**
 * 在 plugins 数组中配置 Japa 插件。
 * 了解更多 - https://japa.dev/docs/runner-config#plugins-optional
 */
export const plugins: Config['plugins'] = [
  assert(),
  pluginAdonisJS(app),
  browserClient({ runInSuites: ['browser'] }),
  sessionBrowserClient(app),
  authBrowserClient(app),
]

/**
 * 配置在所有测试前后运行的
 * 生命周期函数。
 */
export const runnerHooks: Required<Pick<Config, 'setup' | 'teardown'>> = {
  setup: [],
  teardown: [],
}

/**
 * 通过 tap 测试套件实例来配置套件。
 * 了解更多 - https://japa.dev/docs/test-suites#lifecycle-hooks
 */
export const configureSuite: Config['configureSuite'] = (suite) => {
  if (['browser', 'functional', 'e2e'].includes(suite.name)) {
    return suite.setup(() => testUtils.httpServer().start())
  }
}

configureSuite 函数允许你添加特定于某些套件的设置逻辑。在此示例中,browser、functional 和 e2e 套件会在测试运行前自动启动 HTTP 服务器。

创建你的第一个测试

使用 make:test 命令生成一个新的测试文件。

node ace make:test posts/index --suite=browser

这会在 tests/browser/posts/index.spec.ts 创建一个具有以下结构的测试文件。

tests/browser/posts/index.spec.ts
import { test } from '@japa/runner'

test.group('Posts index', () => {
  test('display a list of all posts', async ({ assert }) => {})
})

测试使用 test.group() 组织成组,这有助于构建相关的测试,并允许你应用共享的设置与清理逻辑。单个测试用 test() 定义,并接收一个包含 assert 等实用工具的上文对象。

运行测试

使用以下命令运行你的整个测试套件。

node ace test

要运行特定的套件,将套件名称作为参数传入。

node ace test unit
node ace test browser

筛选测试

Japa 提供了多个用于运行测试子集的标志。

--tests

按精确的测试标题进行筛选。

# 运行标题精确匹配的测试
node ace test --tests="can list all posts"
--files

按测试文件名筛选(匹配文件名末尾,不含 .spec.ts)。--files 标志支持通配符,用于运行目录中的所有测试。

# 运行特定的测试文件
node ace test --files="posts/index"

# 运行 posts 目录中的所有测试
node ace test --files="posts/*"
--groups

按精确的组名进行筛选。

# 运行特定组中的所有测试
node ace test --groups="Posts index"
--tags

按标签进行筛选(前缀 ~ 表示排除)。

--matchAll

要求所有指定的标签都匹配,而非任一匹配。

Watch 模式

在开发过程中,使用 watch 模式可以在文件发生变化时自动重新运行测试。

node ace test --watch

当某个测试文件发生变化时,只重新运行该文件中的测试。当某个源代码文件发生变化时,则运行所有测试。

Tip

如果你正在迭代单个测试,可以将 watch 模式与 --files 筛选器结合使用。这能确保任何文件变更都只运行你关注的测试,从而提供更快的反馈。

附加标志

有两个标志在开发过程中特别有用。

# 在第一次失败时停止
node ace test --bail

# 只重新运行上次运行中失败的测试
node ace test --failed

--bail 标志有助于在调试失败的测试时阻止后续测试运行。--failed 标志可用于快速验证你的修复是否已解决所有失败,而无需运行整个套件。

环境变量

AdonisJS 会在运行测试时自动加载 .env.test 文件,并将其值与标准的 .env 文件合并。任何在 .env.test 中定义的变量都会覆盖 .env 中对应的取值。

在项目根目录创建一个 .env.test 文件来配置测试专属的设置。

.env.test
SESSION_DRIVER=memory

memory 会话驱动在测试中很常用,因为它不会在请求之间持久化会话,从而确保测试的隔离性。你可能会覆盖的其他变量包括数据库连接设置、邮件驱动,或任何在测试期间应有不同行为的服务配置。

下一步

既然你已经了解了 AdonisJS 中测试的配置方式,请继续探索具体的测试指南: