aws-sdk-client-mockとJestを使ったテストコード

test, js, aws
目次

導入・背景

 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",
    });
  });
});

コード解説

モックのセットアップ

mockClientS3Clientをモック化し、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を活用することで、より直感的でメンテナンス性の高いテストコードを実現できます。

参考

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

新卒4年目です。spring boot, jquery, vue を使ってフロントエンド開発、quarkus、azure kubernetesを使ってバックエンドを作ってました。 今は、UXデザイナーを目指して勉強中です! よろしくお願いします。

コメント

コメントする

CAPTCHA


目次