关于 japa
面向 AdonisJS 项目的 Japa 测试技能,涵盖单元、浏览器、API 和控制台测试配置,也提供数据库清理与测试替身的实践指南。
主要功能
- 01配置单元与浏览器测试套件
- 02运行指定套件、文件或测试
- 03设置认证和测试环境
- 04管理测试间数据库状态
- 05指导 API、浏览器和控制台测试
- 06规范测试替身与清理操作
适用场景
- 01为 AdonisJS 项目配置测试套件
- 02筛选运行特定测试
- 03隔离测试间的数据库状态
- 04编写 API 或浏览器测试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ lncitador/ adonisjs-maestro --skill japa安装后在 Claude Code 中输入/japa确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/api-tests.mdbrowser-tests.mdconsole-tests.mdtest-doubles.mdrunbooks/resource-tests.md详细介绍
Japa 测试技能
AdonisJS 使用 Japa——由 AdonisJS 团队构建和维护的后端测试框架。与 Jest/Vitest 不同,Japa 可在 Node.js 中原生运行,无需转译器,并提供专为后端测试设计的插件。
测试套件和结构
tests/
├── bootstrap.ts # plugins, hooks, suite config
├── unit/ # fast in-process tests
└── browser/ # end-to-end with Playwright
在 adonisrc.ts 中定义的测试套件:
tests: {
suites: [
{ files: ['tests/unit/**/*.spec.ts'], name: 'unit', timeout: 2000 },
{ files: ['tests/browser/**/*.spec.ts'], name: 'browser', timeout: 300000 },
],
forceExit: false,
}
bootstrap.ts
Hypermedia/Inertia 起步套件(浏览器和单元测试套件):
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'
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app),
browserClient({ runInSuites: ['browser'] }),
sessionBrowserClient(app),
authBrowserClient(app),
]
export const runnerHooks: Required<Pick<Config, 'setup' | 'teardown'>> = {
setup: [() => testUtils.db().migrate()],
teardown: [],
}
export const configureSuite: Config['configureSuite'] = (suite) => {
if (['browser', 'functional', 'e2e'].includes(suite.name)) {
return suite.setup(() => testUtils.httpServer().start())
}
}
API 起步套件(添加 apiClient 插件):
import { apiClient } from '@japa/api-client'
import { authApiClient } from '@adonisjs/auth/plugins/api_client'
import { sessionApiClient } from '@adonisjs/session/plugins/api_client'
import type { Registry } from '../.adonisjs/client/registry/schema.d.ts'
declare module '@japa/api-client/types' {
interface RoutesRegistry extends Registry {}
}
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app),
apiClient(),
sessionApiClient(app),
authApiClient(app),
]
.env.test——测试中的身份验证始终需要此文件:
SESSION_DRIVER=memory
运行测试
node ace test # all suites
node ace test unit # specific suite
node ace test browser
node ace test --files="posts/index" # specific file
node ace test --files="posts/*" # folder wildcard
node ace test --tests="can list posts" # exact test title
node ace test --groups="Posts index" # group name
node ace test --watch # re-run on change
node ace test --bail # stop on first failure
node ace test --failed # re-run only failed tests
数据库状态——每项测试之间
为每个测试组选择一种方式:
// Global transactions (faster — rolls back, nothing persisted)
group.each.setup(() => testUtils.db().withGlobalTransaction())
// Truncation (actual delete — slower but more compatible)
group.each.setup(() => testUtils.db().truncate())
在 bootstrap.ts 中于所有测试开始前运行一次迁移:
runnerHooks.setup = [
() => testUtils.db().migrate(),
() => testUtils.db().seed(), // optional
]
多个连接:
() => testUtils.db().migrate()
() => testUtils.db('tenant').migrate()
参考资料
| 主题 | 文件 |
|---|---|
| API 测试——client.visit()、断言、身份验证 | references/api-tests.md |
| 浏览器测试——Playwright、表单、loginAs、Cookie | references/browser-tests.md |
| 控制台测试——Ace 命令、原始模式、提示词 | references/console-tests.md |
| 测试替身——伪造、替换、时间、sinon | references/test-doubles.md |
操作指南
| 功能 | 文件 |
|---|---|
| CRUD 资源的完整测试套件 | runbooks/resource-tests.md |
反模式
| 错误做法 | 正确做法 |
|---|---|
| 测试之间不清理数据库 | 在 group.each.setup 中使用 withGlobalTransaction() 或 truncate() |
未将 SESSION_DRIVER 设为 memory | 要让 loginAs 正常工作,必须在 .env.test 中设置 |
使用伪造对象时未使用 using 或 .restore() | 使用 using fake = mail.fake() 自动还原 |
在 group.each.setup 中调用 sinon.restore() | 在 group.each.teardown 中调用 sinon.restore() |
在 group.each.setup 中调用 redis.flushdb() | 在 group.each.teardown 中调用(之后清理,而非之前) |
| 模拟数据库查询 | 访问真实测试数据库——模拟会遗漏约束错误 |
在已提交的测试中使用 record() | 仅用于生成代码——请替换为 visit() |
| 提示陷阱未精确匹配标题 | 标题区分大小写——请从命令中原样复制 |
评论
支持 Markdown