はじめに
今までテストコードを書いたことがほとんどなかったのですが、テストコードを書く機会があったので調べながら実装してみました。
今回は、AdonisJSでユニットテストを書いてみます。
AdonisJSって何?って思った方はこちらの記事を読んでいただければと思います
テストは基本的に以下の3つに分かれます
- ユニットテスト
- インテグレーションテスト
- 機能テスト(フィーチャーテスト、E2Eテストともいう)
やってみた
では、さっそくやってみます!
AdonisJSでは、Japa
というNode.js用のテストフレームワークを使用します。
テストの設定
まず、テストをするための設定からです。
adonisrc.ts
でユニットテストと機能テストで、どのディレクトリ内のファイルのテストを実行するか定義出来ます。
tests: {
suites: [
{
files: ['tests/unit/**/*.spec(.ts|.js)'],
name: 'unit',
timeout: 2000,
},
{
files: ['tests/functional/**/*.spec(.ts|.js)'],
name: 'functional',
timeout: 30000,
},
],
forceExit: false,
},
tests/bootstrap.ts
で、
- configureSuite:テスト実行時に行うsuiteレベルのフック
- runnerHooks:全てのテスト前後でグローバルに行うアクション
-
reporters:テストの進行状況を報告/表示
を設定できます。
export const configureSuite: Config['configureSuite'] = (suite) => {
if (['browser', 'functional', 'e2e'].includes(suite.name)) {
return suite.setup(() => testUtils.httpServer().start())
}
}
export const runnerHooks: Required<Pick<Config, 'setup' | 'teardown'>> = {
setup: [
() => {
console.log('test start')
}
],
teardown: [
() => {
console.log('test end')
}
],
}
export const reporters: Config['reporters'] = {
activated: ['spec'] // spec, dot, ndjson, githubを設定できる
}
テストコードを書いてみる
今回は以下のバリデーションクラスのユニットテストを書いてみます。
import vine from '@vinejs/vine'
const common = {
username: vine.string().trim().minLength(6).maxLength(63).alphaNumeric({
allowDashes: true,
allowSpaces: false,
allowUnderscores: false,
}),
password: vine.string().trim().minLength(8).maxLength(32),
name: vine.string().trim().minLength(6).maxLength(127),
roleId: vine.string().uuid(),
}
/**
* Validates the administrator's creation action
*/
export const createAdministratorValidator = vine.compile(
vine.object({
name: common.name,
username: common.username,
password: common.password,
roleId: common.roleId,
})
)
まず、テストファイルをコマンドで作ります。
--suite
でユニットテストを作るか機能テストを作るか指定します。
node ace make:test validators/AdministratorValidator.ts --suite=unit
# tests/unit/validators/administrator_validator.spec.ts
# node ace make:test controllers/AdministratorController.ts --suite=functional
# tests/functional/validators/administrator_validator.spec.ts
作成したユニットテストのファイルを以下のように修正し、正常系と異常系の1パターンずつ検証するテストコードを実装します。
※実際は、網羅的にやる必要があります
import { Assert } from '@japa/assert';
import { errors, VineValidator } from '@vinejs/vine';
import { SchemaTypes } from '@vinejs/vine/types';
/**
*
* @param {Assert} assert
* @param {VineValidator<SchemaTypes, object>} validator
* @param {object} requestData
* @param {string} errorMessage
* @returns {unknown}
*/
export async function assertValidationError(
assert: Assert,
validator: VineValidator<SchemaTypes, object>,
requestData: object,
errorMessage: string = 'バリデーションエラーにならずに、正常終了しました'
) {
try {
await validator.validate(requestData);
assert.fail(errorMessage); // バリデーションエラーにならない場合は、テスト失敗
} catch (error) {
assert.isTrue(error instanceof errors.E_VALIDATION_ERROR);
// テストしたパラメータが全てバリデーションエラーとなることを確認
const errorFields = error.messages.map((err: any) => err.field);
assert.includeMembers(errorFields, Object.keys(requestData));
return error;
}
};
実行してみる
では、テストコードが書けたので実行してみましょう。
node ace test
# 機能テストの実行
# node ace test functional
# ユニットテストの実行
# node ace test unit
実行すると、全てのテスト(2件)が成功していることがわかります。
[ info ] booting application to run tests...
test start
unit / 管理者登録バリデーション (tests/unit/app/validators/administrator_validator.spec.ts)
✔ 【正常系】文字列が下限の場合 (5.64ms)
✔ 【バリデーションエラー】必須項目が空文字の場合 (4.43ms)
test end
PASSED
Tests 2 passed (2)
Time 160ms
ちなみに、失敗すると以下のようになります。
どこのチェックで失敗しているかも表示してくれますね
[ info ] booting application to run tests...
test start
unit / 管理者登録バリデーション (tests/unit/app/validators/administrator_validator.spec.ts)
✔ 【正常系】文字列が下限の場合 (4.51ms)
✖ 【バリデーションエラー】必須項目が空文字の場合 (8.33ms)
test end
────────────────────────────────────────────────────────────────────────────────────────────────── ERRORS ─────────────────────────────────────────────────────────────────────────────────────────────────────
❯ 管理者登録バリデーション / 【バリデーションエラー】必須項目が空文字の場合
- Expected - 2
+ Received + 1
Array [
- "username",
- "password",
"name",
+ "password",
"roleId",
]
ℹ AssertionError: expected [ 'name', 'password', 'roleId' ] to be a superset of [ Array(4) ]
⁃ at Assert.includeMembers (/workspace/node_modules/@japa/assert/build/index.js:1168:19)
⁃ at Object.executor (tests/unit/app/validators/administrator_validator.spec.ts:48:14)
43 ┃ } catch (error) {
44 ┃ assert.isTrue(error instanceof errors.E_VALIDATION_ERROR);
45 ┃
46 ┃ // テストしたパラメータが全てバリデーションエラーとなることを確認
47 ┃ const errorFields = error.messages.map((err: any) => err.field);
❯ 48 ┃ assert.includeMembers(errorFields, Object.keys(requestData));
49 ┃
50 ┃ return error;
51 ┃ }
52 ┃ });
53 ┃ });
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[1/1]─
FAILED
Tests 1 passed, 1 failed (2)
Time 135ms
おわりに
今回は、AdonisJSでユニットテストを書いてみました。
次は、モックを使ったユニットテストを書いてみようと思います!