導入・背景
AWS を利用したサーバーレスアプリケーション開発では、AWSサービス(S3やDynamoDBなど)との通信が必須です。この通信部分のテストには、APIリクエストをモック化して動作を確認する手法が一般的です。その中でも、効率的で柔軟なテストが可能なパッケージとしてaws-sdk-client-mockが注目されています。
この記事では、aws-sdk-client-mockとJestを使ってAWSサービスと通信する関数のテストを実装する方法を解説します。また、JestのCustom Matcherを活用することで、テストコードをさらに読みやすくする方法も紹介します。
テスト関連のパッケージインストール
まず、テストに必要なパッケージをインストールします。
npm install --save-dev jest aws-sdk-client-mock
また、AWS SDK v3を使用している場合は、その関連パッケージもインストールしてください。
npm install @aws-sdk/client-s3
実行コード(Lambdaと通信)
以下は、S3にファイルをアップロードするシンプルなs3ClientService関数の例です。このコードをテストの対象とします。
const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3");
const s3Client = new S3Client();
exports.handler = async (event) => {
const bucketName = "my-bucket";
const key = event.key;
const body = event.body;
try {
await s3Client.send(new PutObjectCommand({
Bucket: bucketName,
Key: key,
Body: body,
}));
return { statusCode: 200, body: "File uploaded successfully" };
} catch (error) {
return { statusCode: 500, body: "Failed to upload file" };
}
};
aws-sdk-client-mockとJestを使ったテストコード
次に、このs3ClientService関数をテストするコードを作成します。
const { mockClient } = require("aws-sdk-client-mock");
const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3");
const { handler } = require("./lambda");
describe("Lambda Function Tests", () => {
const s3Mock = mockClient(S3Client);
beforeEach(() => {
s3Mock.reset();
});
it("should return success message when file is uploaded", async () => {
s3Mock.on(PutObjectCommand).resolves({});
const response = await handler({ key: "test.txt", body: "Test content" });
expect(response).toEqual({
statusCode: 200,
body: "File uploaded successfully",
});
expect(s3Mock.calls()).toHaveLength(1);
expect(s3Mock.call(0).args[0].input).toEqual({
Bucket: "my-bucket",
Key: "test.txt",
Body: "Test content",
});
});
it("should return error message when upload fails", async () => {
s3Mock.on(PutObjectCommand).rejects(new Error("S3 error"));
const response = await handler({ key: "test.txt", body: "Test content" });
expect(response).toEqual({
statusCode: 500,
body: "Failed to upload file",
});
});
});
コード解説
モックのセットアップ
mockClient
でS3Client
をモック化し、beforeEach
でリセットしています。これにより、各テストが独立して実行可能になります。
成功ケース
s3Mock.on(PutObjectCommand).resolves({});
でS3の成功レスポンスをシミュレートしています。expect
を使って戻り値や呼び出し回数を検証しています。
失敗ケース
s3Mock.on(PutObjectCommand).rejects(new Error("S3 error"));
でエラーをシミュレートし、エラーハンドリングを確認しています。
aws-sdk-client-mockに対応したJestのCustom Matcherを使ってテストする
JestのCustom Matcherを使用すると、テストコードをさらに簡潔にできます。
Custom Matcherを追加
Jestの設定ファイルに以下を記述します。
expect.extend({
toHaveBeenCalledWithInput(received, expectedInput) {
const call = received.calls()[0];
if (call && JSON.stringify(call.args[0].input) === JSON.stringify(expectedInput)) {
return {
message: () => `expected mock not to be called with input ${expectedInput}`,
pass: true,
};
} else {
return {
message: () => `expected mock to be called with input ${expectedInput}`,
pass: false,
};
}
},
});
テストコードの変更
it("should call S3Client with correct parameters", async () => {
s3Mock.on(PutObjectCommand).resolves({});
await handler({ key: "test.txt", body: "Test content" });
expect(s3Mock).toHaveBeenCalledWithInput({
Bucket: "my-bucket",
Key: "test.txt",
Body: "Test content",
});
});
mockとの比較
特徴 | aws-sdk-client-mock | 手動モック |
---|---|---|
セットアップの簡便性 | 高い | 低い |
AWS SDK v3への対応 | 完全対応 | 実装が複雑 |
柔軟性 | 高い(Custom Matcherを使用可能) | 中程度 |
コードの可読性 | 良い | 比較的悪い |
まとめ
aws-sdk-client-mockは、AWS SDK v3を利用するプロジェクトにおいて非常に便利なツールです。特に、Jestと組み合わせることで、テストの効率性と可読性を向上させられます。また、Custom Matcherを活用することで、より直感的でメンテナンス性の高いテストコードを実現できます。
コメント