JestでUUIDの生成処理をテストする
こんにちは、CX事業本部 IoT事業部の若槻です。
Jestは、JavaScriptのテスティングフレームワークです。Facebookが開発しています。
今回は、JestでUUIDの生成処理をテストする方法を確認してみました。
できた方法
テスト対象コード
テスト対象のコードは、下記のようなAWS Step Functionsステートマシンを実行するLambda関数ハンドラーです。
import * as AWS from 'aws-sdk'; import { v4 as uuidv4 } from 'uuid'; const stateMachineArn = process.env.STATE_MACHINE_ARN as string; export const sfn = new AWS.StepFunctions(); export const handler = async (prefix: string): Promise<void> => { const uuid = uuidv4(); await sfn .startExecution({ stateMachineArn: stateMachineArn, name: `${prefix}_${uuid}`, }) .promise(); };
startExecution()
でステートマシンを実行する際に、name
(実行名)を「プレフィクス + UUID」で指定しています。
UUIDの生成に使用しているのは下記のライブラリです。これによりランダムなRFC4122のUUIDを生成できます。
テストコード(uuidをモックする方法)
toBeCalledWith()
を使用して、startExecution()
が期待した引数により実行されているかをテストします。uuid
をモックすることにより固定のUUIDを生成するようにしています。
import { v4 as uuidv4 } from 'uuid'; const stateMachineArn = 'dummyStateMachineArn'; process.env.STATE_MACHINE_ARN = stateMachineArn; import { handler, sfn } from '../src/lambda/handlers/sampleHandler'; jest.mock('uuid'); test('handler', async (): Promise<void> => { (sfn.startExecution as jest.Mock) = jest.fn().mockReturnValue({ promise: jest.fn().mockResolvedValue(undefined), }); (uuidv4 as jest.Mock).mockReturnValue('uuid-123'); await handler('01'); expect(sfn.startExecution).toBeCalledWith({ stateMachineArn: stateMachineArn, name: '01_uuid-123', }); });
テストコード(expect.any()を使用する方法)
こちらのパターンでは、同じくtoBeCalledWith()
を使用していますが、uuid
のモックはしていません。
const stateMachineArn = 'dummyStateMachineArn'; process.env.STATE_MACHINE_ARN = stateMachineArn; import { handler, sfn } from '../src/lambda/handlers/sampleHandler'; test('handler', async (): Promise<void> => { (sfn.startExecution as jest.Mock) = jest.fn().mockReturnValue({ promise: jest.fn().mockResolvedValue(undefined), }); await handler('01'); expect(sfn.startExecution).toBeCalledWith({ stateMachineArn: stateMachineArn, name: expect.any(String), }); });
生成されたUUIDの文字列の部分は、expect.any(constructor)
を使用して任意のString型であることをテストしています。
expect.any(constructor) matches anything that was created with the given constructor. You can use it inside toEqual or toBeCalledWith instead of a literal value.
できたけど宜しくない方法
最初UUIDの生成をするコードのテストを書こうとした時、UUIDをテスト対象コード側でExportして、テストコード側で使うことによりテストを行おうとしました。
テスト対象コード
import * as AWS from 'aws-sdk'; import { v4 as uuidv4 } from 'uuid'; const stateMachineArn = process.env.STATE_MACHINE_ARN as string; export const sfn = new AWS.StepFunctions(); export const uuid = uuidv4(); export const handler = async (prefix: string): Promise<void> => { await sfn .startExecution({ stateMachineArn: stateMachineArn, name: `${prefix}_${uuid}`, }) .promise(); };
テストコード
UUIDをImportしてtoBeCalledWith()
の中で使用しています。これによりuuidのモックおよびexpect.any(constructor)
の使用を不要としました。
const stateMachineArn = 'dummyStateMachineArn'; process.env.STATE_MACHINE_ARN = stateMachineArn; import { handler, sfn, uuid } from '../src/lambda/handlers/sampleHandler'; test('handler', async (): Promise<void> => { (sfn.startExecution as jest.Mock) = jest.fn().mockReturnValue({ promise: jest.fn().mockResolvedValue(undefined), }); await handler('01'); expect(sfn.startExecution).toBeCalledWith({ stateMachineArn: stateMachineArn, name: `01_${uuid}`, }); });
上記のテストコードもパスはするのですが、Lambda関数でハンドラー外で値をUUIDなどを生成すると、下記エントリのようなバグを埋め込むことになるので、宜しくない方法となります。
まとめ
「uuidをモックする方法」であれば生成したUUIDを加工して使用するコードの場合にも、期待した加工ができているかテストができるので、基本的にはこちらの方法を使用すればよいかと思います。
参考
以上