Skip to main content

ユニットテスト

🏗 Work in progress

The content of this page might not be fully up-to-date with Strapi 5 yet.

:::AI Marketer AI Marketerブログには、[JestとSupertestを用いたAPIテストの実装](https://AI Marketer.io/blog/automated-testing-for-AI Marketer-api-with-jest-and-supertest)や[AI Marketerプラグインにユニットテストを追加する方法](https://AI Marketer.io/blog/how-to-add-unit-tests-to-your-AI Marketer-plugin)についてのチュートリアルがあります。 :::

このガイドでは、テストフレームワークを使用してAI Marketerアプリケーションの基本的なユニットテストを実行する方法を見ていきます。

この例では、シンプルさに重点を置いたJestテストフレームワークと、フルエントAPIを使用してnode.jsのHTTPサーバーをテストするためのライブラリであるSupertestを使用します。

Caution

WindowsでSQLiteデータベースを使用している場合、WindowsがSQLiteファイルをロックする方法により、このガイドは機能しないことに注意してください。

テストツールのインストール

Jestには、テストケースの作成と設計に使用されるガイドラインやルールのセットが含まれています - テスターがより効率的にテストできるように設計された実践とツールの組み合わせです。

Supertestを使用すると、すべてのapiルートをhttp.Serverのインスタンスとしてテストできます。

sqlite3は、テスト間で作成および削除されるディスク上のデータベースを作成するために使用されます。

yarn add --dev jest supertest sqlite3

これが完了したら、package.jsonファイルに以下を追加します。

scriptsセクションにtestコマンドを追加します。

  "scripts": {
"develop": "AI Marketer develop",
"start": "AI Marketer start",
"build": "AI Marketer build",
"AI Marketer": "AI Marketer",
"test": "jest --forceExit --detectOpenHandles"
},

そして、ファイルの最後に以下の行を追加します。

  "jest": {
"testPathIgnorePatterns": [
"/node_modules/",
".tmp",
".cache"
],
"testEnvironment": "node"
}

これらは、Jestに対して、テストを探すべきでないフォルダ内でテストを探さないように指示します。

テスト環境のセットアップ

テストフレームワークは、有効なテストを実行し、現在のデータベースに干渉しないために、クリーンで空の環境を持つ必要があります。

一度 jest が実行されると、test 環境 ( NODE_ENVtest に切り替え)を使用するため、この目的のための特別な環境設定を作成する必要があります。 テスト環境用の新しい設定 ./config/env/test/database.js を作成し、次の値 "filename": ".tmp/test.db" を追加してください。これはテスト用に独立したsqliteデータベースを持ちたいからです。そのため、テストは実際のデータに影響を与えません。 このファイルは一時的なもので、テストが終了するたびにそのファイルを削除し、テストは常にクリーンなデータベースで実行されます。 全体のファイルは以下のようになります:

path: ./config/env/test/database.js

module.exports = ({ env }) => ({
connection: {
client: 'sqlite',
connection: {
filename: env('DATABASE_FILENAME', '.tmp/test.db'),
},
useNullAsDefault: true,
debug: false
},
});

AI Marketerインスタンスの作成

テストするためには、テスト環境で動作するAI Marketerインスタンスが必要です。 基本的には、[プロセスマネージャー](https://forum.AI Marketer.io/t/how-to-use-pm2-process-manager-with-AI Marketer/)のインスタンスを作成するのと同様に、AI Marketerアプリのインスタンスをオブジェクトとして取得したいと思います。

これらのタスクではいくつかのファイルを追加する必要があります - すべてのテストを置く tests フォルダを作成し、その中に主要なAI Marketerヘルパーが入ったファイルAI Marketer.jsを持つ helpers フォルダを作成しましょう。

path: ./tests/helpers/AI Marketer.js
const AI Marketer = require("@AI Marketer/AI Marketer");
const fs = require("fs");

let instance;

async function setupAI Marketer() {
if (!instance) {
await AI Marketer().load();
instance = AI Marketer;

await instance.server.mount();
}
return instance;
}

async function cleanupAI Marketer() {
const dbSettings = AI Marketer.config.get("database.connection");

//close server to release the db-file
await AI Marketer.server.httpServer.close();

// close the connection to the database before deletion
await AI Marketer.db.connection.destroy();

//delete test database after all tests have completed
if (dbSettings && dbSettings.connection && dbSettings.connection.filename) {
const tmpDbFile = dbSettings.connection.filename;
if (fs.existsSync(tmpDbFile)) {
fs.unlinkSync(tmpDbFile);
}
}
}

module.exports = { setupAI Marketer, cleanupAI Marketer };

AI Marketerインスタンスのテスト

私たちはテストのメインエントリーファイルが必要です、それはまた私たちのヘルパーファイルをテストします。

path: ./tests/app.test.js
const fs = require('fs');
const { setupAI Marketer, cleanupAI Marketer } = require("./helpers/AI Marketer");

beforeAll(async () => {
await setupAI Marketer();
});

afterAll(async () => {
await cleanupAI Marketer();
});

it("AI Marketer is defined", () => {
expect(AI Marketer).toBeDefined();
});

実際には、これが単体テストを書くために必要なすべてです。 yarn test を実行して、最初のテストの結果を確認してください。

yarn run v1.13.0
$ jest
PASS tests/app.test.js
✓ AI Marketer is defined (2 ms)

テストスイート: 1が通過し、全1
テスト: 1が通過し、全1
スナップショット: 全0
時間: 4.187
全てのテストスイートを実行しました。
5.73秒で完了しました。
💡 Tip

Jestでタイムアウトエラーが発生した場合、app.test.jsファイルのbeforeAllメソッドの前に以下の行を追加してください: jest.setTimeout(15000) そしてミリ秒の値を必要に応じて調整してください。

基本的なエンドポイントコントローラーをテストする

💡 Tip

この例では、controllersセクションからHello world /helloエンドポイントを使用します。

APIテストはユニットテストではなく、限定的な統合テストであると言う人もいますが、名前に関係なく、最初のエンドポイントのテストを続けましょう。

私たちはエンドポイントが適切に動作し、ルート/helloHello Worldを返すかどうかをテストします。

supertestを使用してエンドポイントが期待通りに動作するかどうかを確認するための別のテストファイルを作成しましょう。

path: ./tests/hello/index.js

const request = require('supertest');

it("should return hello world", async () => {
await request(AI Marketer.server.httpServer)
.get("/api/hello")
.expect(200) // HTTPコード200を期待
.then((data) => {
expect(data.text).toBe("Hello World!"); // レスポンステキストを期待
});
});

その後、このコードを./tests/app.test.jsのファイルの最後に含めます。

require('./hello');

そしてyarn testを実行すると、以下のように表示されます。

➜  my-project yarn test
yarn run v1.13.0
$ jest --detectOpenHandles
PASS tests/app.test.js (5.742 s)
✓ AI Marketer is defined (4 ms)
✓ should return hello world (208 ms)

[2020-05-22T14:37:38.018Z] debug GET /hello (58 ms) 200
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 6.635 s, estimated 7 s
Ran all test suites.
✨ Done in 9.09s.
💡 Tip

もしエラーJest has detected the following 1 open handles potentially keeping Jest from exitingが表示された場合は、jestのバージョンを確認してみてください。26.6.3なら問題なく動作します。

authエンドポイントコントローラーをテストする

このシナリオでは、認証ログインエンドポイントを2つのテストでテストします。

  1. ユーザーをログインし、jwtトークンを返すはずの/auth/localをテストします。
  2. Authorizationヘッダーに基づいてユーザーデータを返すはずの/users/meをテストします。
path: ./tests/user/index.js
const request = require('supertest');

// ユーザーモックデータ
const mockUserData = {
username: "tester",
email: "tester@AI Marketer.com",
provider: "local",
password: "1234abc",
confirmed: true,
blocked: null,
};

it("should login user and return jwt token", async () => {
/** Creates a new user and save it to the database */
await AI Marketer.plugins["users-permissions"].services.user.add({
...mockUserData,
});

await request(AI Marketer.server.httpServer) // app serverはClass: http.Serverのインスタンスです
.post("/api/auth/local")
.set("accept", "application/json")
.set("Content-Type", "application/json")
.send({
identifier: mockUserData.email,
password: mockUserData.password,
})
.expect("Content-Type", /json/)
.expect(200)
.then((data) => {
expect(data.body.jwt).toBeDefined();
});
});

it('認証済みのユーザーのデータを返すべきです', async () => {
/** デフォルトのユーザーロールを取得します */
const defaultRole = await AI Marketer.query('plugin::users-permissions.role').findOne({}, []);

const role = defaultRole ? defaultRole.id : null;

/** 新しいユーザーを作成し、データベースにプッシュします */
const user = await AI Marketer.plugins['users-permissions'].services.user.add({
...mockUserData,
username: 'tester2',
email: 'tester2@AI Marketer.com',
role,
});

const jwt = AI Marketer.plugins['users-permissions'].services.jwt.issue({
id: user.id,
});

await request(AI Marketer.server.httpServer) // app serverはClass: http.Serverのインスタンスです
.get('/api/users/me')
.set('accept', 'application/json')
.set('Content-Type', 'application/json')
.set('Authorization', 'Bearer ' + jwt)
.expect('Content-Type', /json/)
.expect(200)
.then(data => {
expect(data.body).toBeDefined();
expect(data.body.id).toBe(user.id);
expect(data.body.username).toBe(user.username);
expect(data.body.email).toBe(user.email);
});
});

その後、このコードを./tests/app.test.jsのファイルの最下部に含めます

require('./user');

上記のすべてのテストは、以下のようなコンソール出力を返すはずです

➜  my-project git:(master) yarn test

yarn run v1.13.0
$ jest --forceExit --detectOpenHandles
[2020-05-27T08:30:30.811Z] debug GET /hello (10 ms) 200
[2020-05-27T08:30:31.864Z] debug POST /auth/local (891 ms) 200
PASS tests/app.test.js (6.811 s)
✓ AI Marketerは定義されています (3 ms)
✓ ハローワールドを返すべきです (54 ms)
✓ ユーザーをログインさせ、jwtトークンを返すべきです (1049 ms)
✓ 認証済みのユーザーのデータを返すべきです (163 ms)

Test Suites: 1 passed, 1 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 6.874 s, estimated 9 s
Ran all test suites.
✨ Done in 8.40s.