導入・背景
SDK for JavaScript v3を使ってLambda関数を書く機会があり、それに伴ってテストコードはどう実装すべきか調べました。
どうやらaws-sdk-client-mockとJestを使うことで比較的簡単に単体テストを作成できるようなので、基本的な使い方についてまとめていきたいと思います。
- aws-sdk-client-mockを使ってみたい方
- これからAWS SDK for JavaScript v3を使ったLambdaのテストコードを書こうと思っている方
テスト関連のパッケージインストール
今回はTypeScriptコードをコンパイル無しにテストする想定です。
Jestの環境構築は公式サイトを参考に作成しています。
npm install --save-dev jest ts-jest @types/jest aws-sdk-client-mock
- jest・・・・・・・Jest本体
- ts-jest・・・・・・TypeScriptでJestを使いたい場合、JestをTSに対応させるために必要なパッケージ
- @types/jest・・・・Jest APIの型定義パッケージ
- aws-sdk-client-mock・・・AWS SDK for JavaScript v3のClientをMockするパッケージ
Lambdaのテスト
実行コード
DynamoDBからUserデータをGetする実行コードをテストしてみます。
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { DynamoDBDocumentClient, GetCommand } from "@aws-sdk/lib-dynamodb"
export const handler = async (event: {userId: string}) => {
const userId = event.userId;
const dynamodb = new DynamoDBClient({});
const ddb = DynamoDBDocumentClient.from(dynamodb)
try {
const result = await ddb.send(
new GetCommand({
TableName: "users",
Key: {
user_id: userId,
},
})
);
return result;
} catch (error) {
console.log("failed get user", error)
throw error;
}
}
aws-sdk-client-mockとJestを使ったテストコード
import { mockClient } from "aws-sdk-client-mock"
import { DynamoDBDocumentClient, GetCommand } from "@aws-sdk/lib-dynamodb"
import { handler } from "./index"
const ddbMock = mockClient(DynamoDBDocumentClient)
describe("get user data", () => {
beforeEach(() => {
ddbMock.reset()
})
it("should get user names", async () => {
const event = {
userId: '0'
}
const expectValue = {
Item: { userId: event.userId, name: "Bob" }
}
ddbMock.on(GetCommand).resolves(expectValue)
const result = await handler(event)
expect(result.Item).toStrictEqual(expectValue.Item)
})
})
コード解説
Mockを作成
import { mockClient } from "aws-sdk-client-mock"
import { DynamoDBDocumentClient, GetCommand } from "@aws-sdk/lib-dynamodb"
const ddbMock = mockClient(DynamoDBDocumentClient)
aws-sdk-client-mockのmockClientを使って、DynamoDBDocumentClientをmock化しておきます。
モックをリセット
beforeEach(() => {
ddbMock.reset()
})
今回のテストケースは1つですが、複数のテストを実行する場合、各テストの実行前にmockの中身をリセットしておきます。
この記述が無いと1つ前のテストのデータが保持されたまま次のテストが実行されるなどエラーの原因となります。
const expectValue = {
Item: { userId: event.userId, name: "Bob" }
}
ddbMock.on(GetCommand).resolves(expectValue)
今回はGetCommandを使うので、mockでGetCommandが実行された場合に、返却される値をresolvesの引数に設定します。
結果をチェック
const result = await handler(event)
expect(result.Item).toStrictEqual(expectValue.Item)
mock化をした実行コードにeventを渡し、実行結果が期待しているデータと一致するかをチェックします。
期待する値するかどうかはJestのtoStrictEqual(Jestが用意しているMachaer)を使ってチェックできます。
呼び出し時の引数をチェック
先ほどは実行コードで返却された値をテストしましたが、GetCommandに渡す引数が期待通りかをチェックしたい場合もあると思います。その場合は以下のようにしてテストします。
// 実行されたGetCommandの情報を取得
const calledGetCommand = ddbMock.commandCalls(GetCommand);
// GetCommand実行時に渡された引数が期待通りかチェック
expect(calledGetCommand[0].args[0].input).toStrictEqual({
TableName: 'users',
Key: { user_id: event.userId}
})
aws-sdk-client-mockに対応したJestのCustom Machaerを使ってテストする
例えばmockが実行したCommandは何か?のような観点でテストを行いたい場合は、以下のパッケージをインストールします。
jest-node18 % npm install -D aws-sdk-client-mock-jest
import 'aws-sdk-client-mock-jest' // import追加
・・・省略・・・
expect(ddbMock).toHaveReceivedCommand(GetCommand)
aws-sdk-client-mockのmockに対応したJestのカスタムマッチャーを使うことで、mockしたClientがGetCommandを呼び出したかテストできます。
テストコード全文
import { mockClient } from "aws-sdk-client-mock"
import { DynamoDBDocumentClient, GetCommand } from "@aws-sdk/lib-dynamodb"
import { handler } from "./index"
import 'aws-sdk-client-mock-jest'
const ddbMock = mockClient(DynamoDBDocumentClient)
describe("get user data", () => {
beforeEach(() => {
ddbMock.reset();
})
it("should get user names", async () => {
const event = {
userId: '0'
}
const expectValue = {
Item: { userId: event.userId, name: "Bob" }
}
ddbMock.on(GetCommand).resolves(expectValue);
const result = await handler(event);
expect(result.Item).toStrictEqual(expectValue.Item);
const calledGetCommand = ddbMock.commandCalls(GetCommand);
expect(calledGetCommand[0].args[0].input).toStrictEqual({
TableName: 'users',
Key: { user_id: event.userId}
})
expect(ddbMock).toHaveReceivedCommand(GetCommand)
})
})
まとめ
今回はaws-sdk-client-mockとJestを使ってSDK for JavaScript v3を使ったLambdaのテストコードを作成しました。aws-sdk-client-mockを使うことで1行でClientをmock化でき、テストコードを簡単に作成できます。DynamoDB以外のサービスでもclientをmockし、コードを実行、各種Commandや結果をテストする流れは同じなので、一度覚えてしまえば他サービスのテストも簡単に行えると思います。
参考
https://github.com/m-radzikowski/aws-sdk-client-mock#dynamodb-documentclient