そもそもやりたかったこと
SQSから受け取ったメッセージを lambdaで加工し、
その情報を使ってDynamoDBからItemをGet、指定のURLへItemの内容をaxiosを使ってPostする仕組みを作っていました。
テストコードを記述するにあたり DynamoDB.DocumentClient.get部分をMock化したいと思いました。
ググるとaws-sdk-mock とか、aws-sdk-client-mockとか情報が出てくるのですがうまくMock化できず詰まりました。
aws-sdk-client-mockはそもそも使っているAWS SDKがv2だったので使えなそうと思い断念。
最終的に jest.mock + JavaScriptのprototypeの組み合わせでうまく行ったので、備忘のため記述します。
環境
lambda: typescript(4.1.3)
テストFW: jest(27.4.7)
aws-sdk: 2.1070.0
mock化したいサンプルコード
import * as AWS from 'aws-sdk';
import { AWSError } from 'aws-sdk';
import { DocumentClient } from 'aws-sdk/clients/dynamodb';
import { PromiseResult } from 'aws-sdk/lib/request';
export const documentClientConfig: AWS.DynamoDB.Types.ClientConfiguration = {
apiVersion: '2012-08-10',
};
export class DDB {
public static async findById(id: string): Promise<PromiseResult<DocumentClient.GetItemOutput, AWSError>> {
const condition: DocumentClient.GetItemInput = {
TableName: 'table_name',
Key: {
ID: id,
},
};
const documentClient: DocumentClient = new AWS.DynamoDB.DocumentClient(documentClientConfig);
return documentClient.get(condition).promise();
}
}
テストコード
import AWS from 'aws-sdk';
import { Handler } from 'path_to_handler/handler';
jest.mock('aws-sdk'); // aws-sdkのMock化。この記述がないとできない
describe('test', () => {
beforeEach(() => {
const ddbData = {
Item: { ID: '1', Data: 'XXX' },
};
const mockDynamoDbGet = {
promise: () => {
return ddbData;
},
};
// Document.Client.getのMock化 .prototypeがポイント
const getMock = AWS.DynamoDB.DocumentClient.prototype.get as jest.Mock;
// Getのレスポンスが返す値を注入。
jest.mocked(getMock).mockReturnValue(mockDynamoDbGet);
});
it('handlerのテスト', async () => {
const event = {id: '1'};
const result = await new Handler(event).execute();
expect(result.ID).toEqual('1');
expect(result.Data).toEqual('XXX');
});
});
まとめ
Mock化したいコンストラクタのprototypeを使うことで解決しました!
prototypeでAWS.DynamoDB.DocumentClientにgetメソッドを持たせて、
getのレスポンスに期待値を注入しています。
prototypeについてはこちらがわかりやすかったです。
終わってみればたった数行で、簡単にDynamoDBのGetがMock化できました。