测试
AdonisJS 内置了对编写测试的支持。你无需安装额外的软件包,也无需为应用做额外的接线配置以支持测试——所有繁重的工作都已经为你完成。
你可以使用以下 ace 命令运行应用的测试。
node ace test
测试文件存放在 tests 目录中,我们进一步按类型来组织测试。例如,功能测试存放在 tests/functional 目录中,单元测试存放在 tests/unit 目录中。
功能测试指的是由外而内的测试,你会向应用发起真实的 HTTP 请求,以测试某个流程或端点的功能。例如,你可能有一组用于创建用户的功能测试。
某些社区可能把功能测试称为特性测试(feature tests)或端到端测试(end-to-end tests)。AdonisJS 对你如何称呼它们持灵活态度。我们决定采用 功能测试 这一术语。
配置测试运行器
AdonisJS 使用 Japa 来编写和运行测试。因此,我们建议阅读 Japa 文档,以便更好地理解它的 API 和配置选项。
测试套件(Suites)
测试套件定义在 adonisrc.ts 文件中。默认情况下,我们注册了 functional 和 unit 两个测试套件。如有需要,你可以移除现有套件并从头开始。
{
tests: {
suites: [
{
name: 'functional',
files: ['tests/functional/**/*.spec.(js|ts)']
},
{
name: 'unit',
files: ['tests/unit/**/*.spec.(js|ts)']
}
]
}
}
- 一个套件由该套件的唯一名称和文件的 glob 匹配模式组合而成。
- 当你为特定套件运行测试时,只会导入与该套件相关的文件。
你可以在运行时使用定义在 tests/bootstrap.ts 文件中的 configureSuite 钩子来配置套件。例如,在运行功能测试时,你可以注册套件级别的钩子来启动 HTTP 服务器。
export const configureSuite: Config['configureSuite'] = (suite) => {
if (['browser', 'functional', 'e2e'].includes(suite.name)) {
return suite.setup(() => testUtils.httpServer().start())
}
}
运行器钩子(Runner hooks)
运行器钩子是可以在所有测试之前和之后运行的全局动作。这些钩子使用 tests/boostrap.ts 文件中的 runnerHooks 属性来定义。
export const runnerHooks: Required<Pick<Config, 'setup' | 'teardown'>> = {
setup: [
() => {
console.log('running before all the tests')
}
],
teardown: [
() => {
console.log('running after all the tests')
}
],
}
插件(Plugins)
Japa 拥有一个插件系统,你可以用它来扩展其功能。插件注册在 tests/bootstrap.ts 文件中。
另见:创建 Japa 插件
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app)
]
报告器(Reporters)
报告器用于在测试运行时报告/展示测试进度。报告器注册在 tests/bootstrap.ts 文件中。
另见:创建 Japa 报告器
export const reporters: Config['reporters'] = {
activated: ['spec']
}
创建测试
你可以使用 make:test 命令创建一个新测试。该命令需要套件的名称来创建测试文件。
另见:make test 命令
node ace make:test posts/create --suite=functional
文件将被创建在通过 files glob 属性配置的目录中。
编写测试
测试使用从 @japa/runner 包导入的 test 方法来定义。一个测试接受标题作为第一个参数,实现回调作为第二个参数。
在以下示例中,我们创建一个新的用户账户,并使用 assert 对象来确保密码被正确哈希。
import { test } from '@japa/runner'
import User from '#models/User'
import hash from '@adonisjs/core/services/hash'
test('hashes user password when creating a new user', async ({ assert }) => {
const user = new User()
user.password = 'secret'
await user.save()
assert.isTrue(hash.isValidHash(user.password))
assert.isTrue(await hash.verify(user.password, 'secret'))
})
使用测试分组
测试分组使用 test.group 方法创建。分组为你的测试增加结构,并允许你在测试周围运行生命周期钩子。
延续前面的示例,让我们把密码哈希测试移入一个分组中。
import { test } from '@japa/runner'
import User from '#models/User'
import hash from '@adonisjs/core/services/hash'
test.group('creating user', () => {
test('hashes user password', async ({ assert }) => {
const user = new User()
user.password = 'secret'
await user.save()
assert.isTrue(hash.isValidHash(user.password))
assert.isTrue(await hash.verify(user.password, 'secret'))
})
})
如果你注意到了,我们从测试标题中移除了 "when creating a new user" 这一片段。这是因为分组标题已经清楚地表明,该分组下的所有测试都限定在 创建新用户 的范畴内。
生命周期钩子
生命周期钩子用于在测试周围执行动作。你可以使用 group 对象来定义钩子。
另见 - Japa 生命周期钩子文档
test.group('creating user', (group) => {
group.each.setup(async () => {
console.log('runs before every test')
})
group.each.teardown(async () => {
console.log('runs after every test')
})
group.setup(async () => {
console.log('runs once before all the tests')
})
group.teardown(async () => {
console.log('runs once after all the tests')
})
test('hashes user password', async ({ assert }) => {
const user = new User()
user.password = 'secret'
await user.save()
assert.isTrue(hash.isValidHash(user.password))
assert.isTrue(await hash.verify(user.password, 'secret'))
})
})
后续步骤
现在你已经了解了创建和编写测试的基础知识。我们建议你在 Japa 文档中探索以下主题。
运行测试
你可以使用 test 命令运行测试。默认情况下,会执行所有套件的测试。不过,你也可以通过传递名称来运行特定套件的测试。
node ace test
node ace test functional
node ace test unit
监听文件变化并重新运行测试
你可以使用 --watch 命令来监听文件系统并重新运行测试。如果某个测试文件发生更改,则会运行该更改文件内的测试。否则,将重新运行所有测试。
node ace test --watch
过滤测试
运行测试时,你可以使用命令行标志来应用过滤器。以下是可用选项的列表。
另见:Japa 测试过滤指南
使用 VSCode? 使用 Japa 扩展,即可通过键盘快捷键或活动侧边栏在代码编辑器中运行选中的测试。
| 标志 | 描述 |
|---|---|
--tests | 按测试标题过滤测试。该过滤器针对完全一致的测试标题进行匹配。 |
--files | 按测试文件名的子集过滤测试。匹配针对去掉 .spec.ts 后的文件名末尾进行。你可以使用通配符表达式运行整个文件夹的测试。folder/* |
--groups | 按分组名称过滤测试。该过滤器针对完全一致的分组名称进行匹配。 |
--tags | 按标签过滤测试。你可以在标签名前加上波浪号 ~ 来忽略带有给定标签的测试。 |
--matchAll | 默认情况下,Japa 会运行匹配所提及任一标签的测试。如果你希望所有标签都匹配,则使用 --matchAll 标志。 |
强制退出测试
Japa 会在所有测试完成后等待进程优雅关闭。优雅关闭过程意味着退出所有长连接并清空 Node.js 事件循环。
如有需要,你可以使用 --force-exit 标志强制 Japa 退出进程,而不等待优雅关闭。
node ace test --force-exit
重试测试
你可以使用 --retries 标志多次重试失败的测试。该标志将应用于所有未在测试级别显式定义重试次数的测试。
# Retry failing tests 2 times
node ace test --retries=2
运行上次运行中失败的测试
你可以使用 --failed 命令行标志重新运行上次运行中失败的测试。
node ace test --failed
在报告器之间切换
Japa 允许你在配置文件中注册多个测试报告器,但默认不会激活它们。你可以在配置文件中激活报告器,或使用 --reporter 命令行标志。
# Activate spec reporter
node ace test --reporter=spec
# Activate spec and json reporters
node ace test --reporter=spec,json
你也可以在配置文件中激活报告器。
export const reporters: Config['reporters'] = {
activated: ['spec', 'json']
}
向 Node.js 命令行传递选项
test 命令会以子进程的方式运行测试 (bin/test.ts file)。如果你想向子进程传递 node 参数,可以在命令名之前定义它们。
node ace --no-warnings --trace-exit test
环境变量
你可以使用 .env.test 文件来定义测试期间所需的环境变量。.env.test 中的值优先于 .env 文件中的值。
测试期间的 SESSION_DRIVER 必须设置为 memory。
SESSION_DRIVER=memory