導入・背景
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を活用することで、より直感的でメンテナンス性の高いテストコードを実現できます。


 
			 
			 
			 
			 
			 
			 
			 
			 
			
コメント