☁️

TypeScript + Jestでaws-sdkをmockする

2021/04/19に公開

この記事について

最近よくTypeScriptでAWSのリソース操作を行うのですが、動作確認を行うたびにリソースを作ったり消したりするのが面倒くさいと感じていました。
ましてやユニットコードなんて書いてもその時のリソース状況にがっつり依存するなあ・・・と少しナイーブになっていました。

そんな中、最近やっとaws-sdkのモック化に成功したのでメモとして残しておきます。

動作確認元のDynamoDBテーブル定義や詳細はこの記事を参照してください。
CloudFormationの内容と実プロジェクトのリポジトリを記載しています。

Jest

https://jestjs.io/
Facebook製のテストフレームワークです。
概要や基本的な書き方はこの記事が参考になります。

Jest導入

yarn add jest @types/jest ts-jest -D

jest.config.js作成

module.exports = {
    roots: ["<rootDir>/test", "<rootDir>/src"],
    transform: {
        "^.+\\.tsx?$": "ts-jest",
    },
    testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"],
    moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
};

実装

今回はDynamoDBのテーブルをscanし、項目の数によって返却する値を変えるシンプルなコードを書きます。
src/index.ts

import { DynamoDB } from "aws-sdk";
export const handler = async (event: any) => {
  let scanItem = await new DynamoDB()
    .scan({ TableName: "SampleTable", Select: "ALL_ATTRIBUTES" })
    .promise();
  if (scanItem.Count === 1) {
    return "1";
  } else if (scanItem.Count === 2) {
    return "2";
  }
  return "etc";
};

テストコード実装

jest.config.jsで定義したパスにファイルを作成します。
test/scan-dynamodb.test

import { DynamoDB } from "aws-sdk";
import { mocked } from "ts-jest/utils";

import { handler } from "../src/index";

jest.mock("aws-sdk");

describe("scan test", () => {
  it("Count => 1 : return 1", async () => {
    let data = {
      Items: [{ value: "Value1", SampleKey: "key1" }],
      Count: 1,
      ScannedCount: 1,
    };
    mocked(DynamoDB).mockImplementationOnce((): any => {
      return {
        scan: (_param: any, callback: any) => {
          return {
            promise: () => {
              return data;
            },
          };
        },
      };
    });

    const result = await handler({});
    expect(result).toEqual("1");
  });
  it("Count => 2 : return 2", async () => {
    let data = {
      Items: [
        { value: "Value1", SampleKey: "key1" },
        { value: "Value2", SampleKey: "key2" },
      ],
      Count: 2,
      ScannedCount: 2,
    };
    mocked(DynamoDB).mockImplementationOnce((): any => {
      return {
        scan: (_param: any, callback: any) => {
          return {
            promise: () => {
              return data;
            },
          };
        },
      };
    });

    const result = await handler({});
    expect(result).toEqual("2");
  });
  it("Count => 3 : return etc", async () => {
    let data = {
      Items: [
        { value: "Value1", SampleKey: "key1" },
        { value: "Value2", SampleKey: "key2" },
        { value: "Value3", SampleKey: "key3" },
      ],
      Count: 3,
      ScannedCount: 3,
    };
    mocked(DynamoDB).mockImplementationOnce((): any => {
      return {
        scan: (_param: any, callback: any) => {
          return {
            promise: () => {
              return data;
            },
          };
        },
      };
    });

    const result = await handler({});
    expect(result).toEqual("etc");
  });
});

mocked(DynamoDB).mockImplementationOnceでDynamoDBのscan関数をmock化しています。

実行

yarn jest
yarn run v1.22.4
 PASS  test/scan-dynamodb.test.ts (10.408 s)
  scan test
    √ Count => 1 : return 1 (5 ms)
    √ Count => 2 : return 2
    √ Count => 3 : return etc (1 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        11.903 s
Ran all test suites.
Done in 13.56s.

しっかり通りました。

所感

できるまで非常に時間がかかりましたが、やってみると意外とシンプルに完結できることがわかりました。
実際にデータを用意せずにaws-sdkを使用したロジックの動作確認が実施できるようになって結構テンションがあがりました🍣

GitHubで編集を提案

Discussion