HTTP テスト

HTTPテスト

HTTPテストは、実際のHTTPリクエストを使用してアプリケーションのエンドポイントをテストし、レスポンスのボディ、ヘッダー、クッキー、セッションなどをアサートすることを指します。

HTTPテストは、JapaのAPIクライアントプラグインを使用して実行されます。APIクライアントプラグインは、Axiosfetchに似た状態を持たないリクエストライブラリであり、テストに適しています。

実際のブラウザ内でWebアプリをテストし、プログラムで対話する場合は、Playwrightを使用したブラウザクライアントを使用することをおすすめします。

セットアップ

最初のステップは、npmパッケージレジストリから次のパッケージをインストールすることです。

npm i -D @japa/api-client

プラグインの登録

次に、tests/bootstrap.tsファイル内でプラグインを登録します。

tests/bootstrap.ts
import { apiClient } from '@japa/api-client'
export const plugins: Config['plugins'] = [
assert(),
apiClient(),
pluginAdonisJS(app),
]

apiClientメソッドは、オプションでサーバーのbaseURLを受け入れます。指定しない場合、HOSTPORTの環境変数が使用されます。

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=functional
import { test } from '@japa/runner'
test.group('ユーザーリスト', () => {
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ファイル内のassertプラグインで登録します。

tests/bootstrap.ts
import app from '@adonisjs/core/services/app'
export const plugins: Config['plugins'] = [
assert({
openApi: {
schemas: [app.makePath('resources/open_api_schema.yaml')]
}
}),
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' }]
})

クッキーの読み取り/書き込み

withCookieメソッドを使用して、HTTPリクエスト中にクッキーを送信することができます。メソッドは、最初の引数としてクッキーの名前、2番目の引数として値を受け入れます。

await client
.get('/users')
.withCookie('user_preferences', { limit: 10 })

withCookieメソッドは、署名付きクッキーを定義します。さらに、withEncryptedCookieメソッドまたはwithPlainCookieメソッドを使用して、他のタイプのクッキーをサーバーに送信することもできます。

await client
.get('/users')
.witEncryptedCookie('user_preferences', { limit: 10 })
await client
.get('/users')
.withPlainCookie('user_preferences', { limit: 10 })

レスポンスからクッキーを読み取る

AdonisJSサーバーが設定したクッキーには、response.cookiesメソッドを使用してアクセスできます。メソッドは、クッキーをキーと値のペアとして返します。

const response = await client.get('/users')
console.log(response.cookies())

response.cookieメソッドを使用して、クッキーの名前で単一のクッキー値にアクセスすることもできます。または、assertCookieメソッドを使用してクッキーの値をアサートすることもできます。

const response = await client.get('/users')
console.log(response.cookie('user_preferences'))
response.assertCookie('user_preferences')

セッションストアのポピュレート

アプリケーションでセッションデータを読み取り/書き込みするために@adonisjs/sessionパッケージを使用している場合、テストを作成する際にセッションストアをポピュレートするためにsessionApiClientプラグインを使用することもできます。

セットアップ

最初のステップは、tests/bootstrap.tsファイル内でプラグインを登録することです。

tests/bootstrap.ts
import { sessionApiClient } from '@adonisjs/session/plugins/api_client'
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app),
sessionApiClient(app)
]

次に、.env.testファイルを更新し(存在しない場合は作成)、SESSON_DRIVERmemoryに設定します。

.env.test
SESSION_DRIVER=memory

セッションデータを使用したリクエストの作成

Japa APIクライアントのwithSessionメソッドを使用して、あらかじめ定義されたセッションデータを使用してHTTPリクエストを行うことができます。

withSessionメソッドは、新しいセッションIDを作成し、データをメモリストアにポピュレートし、AdonisJSアプリケーションコードは通常どおりセッションデータを読み取ることができます。

リクエストが完了すると、セッションIDとそのデータは破棄されます。

test('カートアイテムを持つチェックアウト', async ({ client }) => {
await client
.post('/checkout')
.withSession({
cartItems: [
{
id: 1,
name: 'South Indian Filter Press Coffee'
},
{
id: 2,
name: 'Cold Brew Bags',
}
]
})
})

withSessionメソッドと同様に、withFlashMessagesメソッドを使用してHTTPリクエスト時にフラッシュメッセージを設定することもできます。

const response = await client
.get('posts/1')
.withFlashMessages({
success: '投稿が正常に作成されました'
})
response.assertTextIncludes(`投稿が正常に作成されました`)

レスポンスからセッションデータを読み取る

AdonisJSサーバーが設定したセッションデータには、response.session()メソッドを使用してアクセスできます。メソッドは、セッションデータをキーと値のペアのオブジェクトとして返します。

const response = await client
.post('/posts')
.json({
title: 'some title',
body: 'some description',
})
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ファイル内でプラグインを登録することです。

tests/bootstrap.ts
import { authApiClient } from '@adonisjs/auth/plugins/api_client'
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app),
authApiClient(app)
]

セッションベースの認証を使用している場合は、セッションドライバをインメモリストアに切り替える必要があります。

.env.test
SESSION_DRIVER=memory

以上です。loginAsメソッドを使用してユーザーをログインすることができます。メソッドは、ユーザーオブジェクトを唯一の引数として受け入れ、現在のHTTPリクエストのためにユーザーをログイン済みとしてマークします。

import User from '#models/user'
test('支払いリストを取得する', async ({ client }) => {
const user = await User.create(payload)
await client
.get('/me/payments')
.loginAs(user)
})

loginAsメソッドは、認証にconfig/auth.tsファイルで設定されたデフォルトのガードを使用します。ただし、withGuardメソッドを使用してカスタムガードを指定することもできます。例:

await client
.get('/me/payments')
.withGuard('api_tokens')
.loginAs(user)

CSRFトークンを使用したリクエストの作成

もしアプリケーションのフォームがCSRF保護を使用している場合、withCsrfTokenメソッドを使用してCSRFトークンを生成し、リクエスト中にヘッダーとして渡すことができます。

withCsrfTokenメソッドを使用する前に、tests/bootstrap.tsファイル内で以下のJapaプラグインを登録し、またセッションドライバのSESSION_DRIVER環境変数memoryに切り替えることを確認してください。

tests/bootstrap.ts
import { shieldApiClient } from '@adonisjs/shield/plugins/api_client'
import { sessionApiClient } from '@adonisjs/session/plugins/api_client'
export const plugins: Config['plugins'] = [
assert(),
pluginAdonisJS(app),
sessionApiClient(app),
shieldApiClient()
]
test('投稿を作成する', async ({ client }) => {
await client
.post('/posts')
.form(dataGoesHere)
.withCsrfToken()
})

ルートヘルパー

TestContextのrouteヘルパーを使用すると、ルートのURLを作成することができます。routeヘルパーを使用することで、ルートを更新する際にテスト内のすべてのURLを修正する必要がなくなります。

routeヘルパーは、routeと同じ引数を受け入れます。

test('ユーザーのリストを取得する', async ({ client, route }) => {
const response = await client.get(
route('users.list')
)
response.assertStatus(200)
response.assertBody({
data: [
{
id: 1,
email: 'foo@bar.com',
}
]
})
})