e2e(エンドツーエンド)テスト
個々のモジュールやクラスに焦点を当てた単体テストとは異なり、エンドツーエンド (e2e) テストは、クラスとモジュールの相互作用をより集合的なレベルでカバーします。これは、エンドユーザーが本番環境と行う相互作用に近いものです。システム。アプリケーションが成長するにつれて、各 API エンドポイントのエンドツーエンドの動作を手動でテストすることが困難になります。自動化されたエンドツーエンド テストは、システムの全体的な動作が正しく、プロジェクトの要件を満たしていることを確認するのに役立ちます。
e2eテストは実際の環境でAPIをテストするため、サービスをモックする必要はありません。e2eテストを書く主なステップは以下の通りです:
- 必要なデータを作成します。
- Supertest ライブラリを使用して API 呼び出しを行い、HTTP リクエストをシミュレートします。
- データが正しいかどうかを確認してください
- データをクリアする
テストの実行順序
データベース状態を共有するE2Eテストは、レース条件を避けるために順次実行する必要があります。並列実行を無効にするには --runInBand フラグを使用します:
# Run E2E tests sequentially (E2Eテストを順次実行)
jest --runInBand test/e2e
# Update the test:e2e script in package.json (package.json の test:e2e スクリプトを更新。生成時のデフォルトには --runInBand が含まれていません)
"test:e2e": "jest --runInBand --config ./test/jest-e2e.json"
--runInBand がない場合、Jestは複数のワーカー間でテストを並列実行し、以下の問題が発生する可能性があります:
- テストが同じレコードを変更する際のデータ競合
- タイミングの問題による不安定なテスト
- クリーンアップ操作が実行中の他のテストに影響する
e2e テストの基本構造を以下に示します:
import request from "supertest";
import { config } from "./config";
import {
deleteItem,
getItem,
getTableName,
putItem,
TableType,
} from "./dynamo-client";
import { syncDataFinished } from "./utils";
const API_PATH = "/api/cat";
describe("Cat", () => {
it("should create a new cat (新しい猫を作成する)", async () => {
// Arrange - define test data inline (テストデータをインラインで定義)
const payload = {
pk: "CAT#TENANT1",
sk: "cat#001",
id: "CAT#TENANT1#cat#001",
name: "Whiskers",
version: 0,
code: "cat#001",
type: "CAT",
};
// Action - make API call (APIを呼び出す)
const res = await request(config.apiBaseUrl).post(API_PATH).send(payload);
// Assert - verify response and data (レスポンスとデータを検証)
expect(res.statusCode).toEqual(201);
// Wait for async processing to complete (非同期処理の完了を待つ)
await syncDataFinished("cat_table", {
pk: payload.pk,
sk: `${payload.sk}@1`,
});
// Verify data in DynamoDB (DynamoDBのデータを検証)
const data = await getItem(getTableName("cat_table", TableType.DATA), {
pk: payload.pk,
sk: payload.sk,
});
expect(data).toMatchObject({
...payload,
version: 1,
});
}, 40000);
// Clean up test data if needed (必要に応じてテストデータをクリーンアップ)
afterAll(async () => {
await deleteItem(getTableName("cat_table", TableType.DATA), {
pk: "CAT#TENANT1",
sk: "cat#001",
});
});
});
テストヘルパーユーティリティ
フレームワークはe2eテスト用のヘルパーユーティリティ を提供しています:
config.ts - 環境設定
import dotenv from "dotenv";
dotenv.config();
const config = {
nodeEnv: process.env.NODE_ENV,
appName: process.env.APP_NAME,
dynamoEndpoint: process.env.DYNAMODB_ENDPOINT,
dynamoRegion: process.env.DYNAMODB_REGION,
apiBaseUrl: process.env.API_BASE_URL || "http://0.0.0.0:3000",
};
export { config };
dynamo-client.ts - DynamoDB操作
import {
DynamoDBClient,
GetItemCommand,
PutItemCommand,
DeleteItemCommand,
} from "@aws-sdk/client-dynamodb";
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
import { config } from "./config";
const tablePrefix = `${config.nodeEnv}-${config.appName}-`;
enum TableType {
COMMAND = "command",
DATA = "data",
HISTORY = "history",
}
const dynamoClient = new DynamoDBClient({
endpoint: config.dynamoEndpoint,
region: config.dynamoRegion,
});
const getTableName = (tableName: string, tableType: TableType) => {
return `${tablePrefix}${tableName}-${tableType}`;
};
const getItem = async (tableName: string, key: { pk: string; sk: string }) => {
const { Item } = await dynamoClient.send(
new GetItemCommand({
TableName: tableName,
Key: marshall(key),
})
);
return Item ? unmarshall(Item) : undefined;
};
export { getItem, getTableName, TableType, dynamoClient };