HTTP 测试
HTTP 测试是指通过对应用程序端点发出实际的 HTTP 请求并断言响应体、头、cookie、会话等来测试它们。
HTTP 测试使用 Japa 的 API 客户端插件执行。API 客户端插件是一个无状态请求库,类似于 Axios 或 fetch,但更适合测试。
如果您想在真实浏览器中测试 Web 应用程序并以编程方式与它们交互,我们建议使用使用 Playwright 进行测试的浏览器客户端。
设置
第一步是从 npm 包注册表安装以下包。
:::codegroup
// title: npm
npm i -D @japa/api-client:::
注册插件
在继续之前,请在 tests/bootstrap.ts 文件中注册插件。
// title: tests/bootstrap.ts
import { apiClient } from '@japa/api-client'
export const plugins: Config['plugins'] = [
assert(),
// highlight-start
apiClient(),
// highlight-end
pluginAdonisJS(app),
]apiClient 方法可选地接受服务器的 baseURL。如果未提供,它将使用 HOST 和 PORT 环境变量。
import env from '#start/env'
export const plugins: Config['plugins'] = [
apiClient({
baseURL: `http://${env.get('HOST')}:${env.get('PORT')}`
})
]基本示例
注册 apiClient 插件后,您可以从 TestContext 访问 client 对象来发出 HTTP 请求。
HTTP 测试必须写在为 functional 测试套件配置的文件夹中。您可以使用以下命令创建新的测试文件。
node ace make:test users/list --suite=functionalimport { test } from '@japa/runner'
test.group('Users list', () => {
test('获取用户列表', async ({ client }) => {
const response = await client.get('/users')
response.assertStatus(200)
response.assertBody({
data: [
{
id: 1,
email: 'foo@bar.com',
}
]
})
})
})要查看所有可用的请求和断言方法,请确保阅读 Japa 文档。
Open API 测试
断言和 API 客户端插件允许您使用 Open API 规范文件编写断言。您可以使用规范文件来测试 HTTP 响应的形状,而不是手动针对固定负载测试响应。
这是保持 Open API 规范和服务器响应同步的好方法。因为如果您从规范文件中删除某个端点或更改响应数据形状,您的测试将失败。
注册模式
AdonisJS 不提供从代码生成 Open API 模式文件的工具。您可以手动编写它或使用图形工具来创建它。
一旦您有了规范文件,将其保存在 resources 目录中(如果缺少则创建该目录),并在 tests/bootstrap.ts 文件中使用 openapi-assertions 插件注册它。
npm i -D @japa/openapi-assertions// title: tests/bootstrap.ts
import app from '@adonisjs/core/services/app'
// highlight-start
import { openapi } from '@japa/openapi-assertions'
// highlight-end
export const plugins: Config['plugins'] = [
assert(),
// highlight-start
openapi({
schemas: [app.makePath('resources/open_api_schema.yaml')]
})
// highlight-end
apiClient(),
pluginAdonisJS(app)
]编写断言
注册模式后,您可以使用 response.assertAgainstApiSpec 方法对 API 规范进行断言。
test('分页帖子', async ({ client }) => {
const response = await client.get('/posts')
response.assertAgainstApiSpec()
})response.assertAgainstApiSpec方法将使用请求方法、端点和响应状态码来查找预期的响应模式。- 当无法找到响应模式时将引发异常。否则,将根据模式验证响应体。
只测试响应的形状,而不是实际值。因此,您可能需要编写额外的断言。例如:
// 断言响应符合模式
response.assertAgainstApiSpec()
// 断言预期值
response.assertBodyContains({
data: [{ title: 'Adonis 101' }, { title: 'Lucid 101' }]
})读取/写入 cookie
您可以在 HTTP 请求期间使用 withCookie 方法发送 cookie。该方法接受 cookie 名称作为第一个参数,值作为第二个参数。
await client
.get('/users')
.withCookie('user_preferences', { limit: 10 })withCookie 方法定义一个签名 cookie。此外,您可以使用 withEncryptedCookie 或 withPlainCookie 方法向服务器发送其他类型的 cookie。
await client
.get('/users')
.withEncryptedCookie('user_preferences', { limit: 10 })await client
.get('/users')
.withPlainCookie('user_preferences', { limit: 10 })从响应中读取 cookie
您可以使用 response.cookies 方法访问由 AdonisJS 服务器设置的 cookie。该方法将 cookie 作为键值对对象返回。
const response = await client.get('/users')
console.log(response.cookies())您可以使用 response.cookie 方法按名称访问单个 cookie 值。或使用 assertCookie 方法断言 cookie 值。
const response = await client.get('/users')
console.log(response.cookie('user_preferences'))
response.assertCookie('user_preferences')填充会话存储
如果您使用 @adonisjs/session 包在应用程序中读取/写入会话数据,您可能还需要使用 sessionApiClient 插件在编写测试时填充会话存储。
设置
第一步是在 tests/bootstrap.ts 文件中注册插件。
// title: tests/bootstrap.ts
// insert-start
import { sessionApiClient } from '@adonisjs/session/plugins/api_client'
// insert-end
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app),
// insert-start
sessionApiClient(app)
// insert-end
]然后,更新 .env.test 文件(如果缺少则创建一个)并将 SESSON_DRIVER 设置为 memory。
// title: .env.test
SESSION_DRIVER=memory使用会话数据发出请求
您可以使用 Japa API 客户端上的 withSession 方法使用一些预定义的会话数据发出 HTTP 请求。
withSession 方法将创建一个新的会话 ID 并使用数据填充内存存储,您的 AdonisJS 应用程序代码可以像往常一样读取会话数据。
请求完成后,会话 ID 及其数据将被销毁。
test('使用购物车商品结账', async ({ client }) => {
await client
.post('/checkout')
// highlight-start
.withSession({
cartItems: [
{
id: 1,
name: '南印度滤压咖啡'
},
{
id: 2,
name: '冷萃咖啡袋',
}
]
})
// highlight-end
})与 withSession 方法类似,您可以使用 withFlashMessages 方法在发出 HTTP 请求时设置闪存消息。
const response = await client
.get('posts/1')
.withFlashMessages({
success: '帖子创建成功'
})
response.assertTextIncludes(`帖子创建成功`)从响应中读取会话数据
您可以使用 response.session() 方法访问由 AdonisJS 服务器设置的会话数据。该方法将会话数据作为键值对对象返回。
const response = await client
.post('/posts')
.json({
title: '某个标题',
body: '某个描述',
})
console.log(response.session()) // 所有会话数据
console.log(response.session('post_id')) // post_id 的值您可以使用 response.flashMessage 或 response.flashMessages 方法从响应中读取闪存消息。
const response = await client.post('/posts')
response.flashMessages()
response.flashMessage('errors')
response.flashMessage('success')最后,您可以使用以下方法之一为会话数据编写断言。
const response = await client.post('/posts')
/**
* 断言会话存储中存在特定键(带可选值)
*/
response.assertSession('cart_items')
response.assertSession('cart_items', [{
id: 1,
}, {
id: 2,
}])
/**
* 断言会话存储中不存在特定键
*/
response.assertSessionMissing('cart_items')
/**
* 断言闪存消息存储中存在闪存消息(带可选值)
*/
response.assertFlashMessage('success')
response.assertFlashMessage('success', '帖子创建成功')
/**
* 断言闪存消息存储中不存在特定键
*/
response.assertFlashMissing('errors')
/**
* 断言闪存消息存储中的验证错误
*/
response.assertHasValidationError('title')
response.assertValidationError('title', '请输入帖子标题')
response.assertValidationErrors('title', [
'请输入帖子标题',
'帖子标题必须至少 10 个字符长。'
])
response.assertDoesNotHaveValidationError('title')认证用户
如果您使用 @adonisjs/auth 包在应用程序中认证用户,您可以使用 authApiClient Japa 插件在向应用程序发出 HTTP 请求时认证用户。
第一步是在 tests/bootstrap.ts 文件中注册插件。
// title: tests/bootstrap.ts
// insert-start
import { authApiClient } from '@adonisjs/auth/plugins/api_client'
// insert-end
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app),
// insert-start
authApiClient(app)
// insert-end
]如果您使用基于会话的认证,请确保也设置会话插件。参见填充会话存储 - 设置。
就是这样。现在,您可以使用 loginAs 方法登录用户。该方法接受用户对象作为唯一参数,并将用户标记为当前 HTTP 请求的已登录状态。
import User from '#models/user'
test('获取付款列表', async ({ client }) => {
const user = await User.create(payload)
await client
.get('/me/payments')
// highlight-start
.loginAs(user)
// highlight-end
})loginAs 方法使用在 config/auth.ts 文件中配置的默认守卫进行认证。但是,您可以使用 withGuard 方法指定自定义守卫。例如:
await client
.get('/me/payments')
// highlight-start
.withGuard('api_tokens')
.loginAs(user)
// highlight-end使用 CSRF 令牌发出请求
如果应用程序中的表单使用 CSRF 保护,您可以使用 withCsrfToken 方法生成 CSRF 令牌并在请求期间将其作为头传递。
在使用 withCsrfToken 方法之前,请在 tests/bootstrap.ts 文件中注册以下 Japa 插件,并确保将 SESSION_DRIVER 环境变量切换为 memory。
// title: tests/bootstrap.ts
// insert-start
import { shieldApiClient } from '@adonisjs/shield/plugins/api_client'
import { sessionApiClient } from '@adonisjs/session/plugins/api_client'
// insert-end
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app),
// insert-start
sessionApiClient(app),
shieldApiClient()
// insert-end
]test('创建帖子', async ({ client }) => {
await client
.post('/posts')
.form(dataGoesHere)
.withCsrfToken()
})route 助手
您可以使用 TestContext 中的 route 助手为路由创建 URL。使用 route 助手可确保每当您更新路由时,无需返回并修复测试中的所有 URL。
route 助手接受与全局模板方法 route 接受的相同参数集。
test('获取用户列表', async ({ client, route }) => {
const response = await client.get(
// highlight-start
route('users.list')
// highlight-end
)
response.assertStatus(200)
response.assertBody({
data: [
{
id: 1,
email: 'foo@bar.com',
}
]
})
})