ダミーのデータを返すREST APIをAPI GatewayのMock Integrationで作ってテストで使う
こんにちは、CX事業本部 IoT事業部の若槻です。
前回のエントリではAWS Lambda関数のテスト時に環境変数の設定値を一時的に変更する方法を紹介しました。
実際には、その環境変数は外部のAPIエンドポイントの情報であり、Lambda関数はそのAPIからのデータ取得を行うものでした。しかしテスト時に実際のAPIエンドポイントを叩かせたくない場合は、テスト用のダミーのREST APIが必要ですね。
そこで今回は、ダミーのデータを返すREST APIをAPI Gateway Mock Integrationで作って、テスト時にそれをLambda関数が叩くようにしてみました。
リソースの作成
テスト対象となるLambda関数とダミーのREST APIのリソースをAWS CDKで作成します。
import { aws_apigateway, aws_lambda_nodejs, Stack, StackProps, } from 'aws-cdk-lib'; import { Construct } from 'constructs'; export class AwsCdkAppStack extends Stack { constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); //Lambda関数 new aws_lambda_nodejs.NodejsFunction(this, 'hogeFunc', { functionName: 'hogeFunc', entry: 'src/hoge.ts', environment: { HOGE_API_ENDPOINT: 'https://api.example.com/data', }, }); //ダミーのREST API const dummyApi = new aws_apigateway.RestApi(this, 'dummyApi', { restApiName: 'dummyApi', deployOptions: { stageName: 'v1', }, }); const data = dummyApi.root.addResource('data'); //Mock Integrationを設定 data.addMethod( 'GET', new aws_apigateway.MockIntegration({ requestTemplates: { 'application/json': '{ "statusCode": 200 }', }, integrationResponses: [ { statusCode: '200', responseTemplates: { 'application/json': '{}', //テスト実施時にはここにダミーデータを設定 }, }, ], }), { methodResponses: [{ statusCode: '200' }], } ); } }
- aws_apigateway.MockIntegrationクラスを使ってMock Integrationを作っています。テスト用の固定のデータを返せれば良いので、APIのバックにLambdaやデータベースを置く必要がないため、Mock Integrationを使用しています。
Lambda関数のハンドラーでは環境変数HOGE_API_ENDPOINT
から取得したAPIエンドポイントを叩いてデータを取得します。
import Axios from 'axios'; const HOGE_API_ENDPOINT = process.env.HOGE_API_ENDPOINT || ''; export const handler = async (): Promise<void> => { console.log(HOGE_API_ENDPOINT); const response = await Axios.get(HOGE_API_ENDPOINT); console.log(response.data); };
CDK DeployしてLambda関数とREST APIを作成します。
AWSマネジメントコンソールを見ると、REST APIとリソースが作成されていることが確認できます。それぞれのIDを控えます。
またここでGETメソッドのテストをしてみます。
ちゃんとステータス200でレスポンスボディが返りました。良さそうです。
テスト
Jestのテストコードは次のようになります。
import { GetFunctionConfigurationCommand, InvokeCommand, LambdaClient, UpdateFunctionConfigurationCommand, } from '@aws-sdk/client-lambda'; import { APIGatewayClient, UpdateIntegrationResponseCommand, CreateDeploymentCommand, } from '@aws-sdk/client-api-gateway'; const lambdaClient = new LambdaClient({ region: 'ap-northeast-1' }); const apiGatewayClient = new APIGatewayClient({ region: 'ap-northeast-1' }); const DUMMY_REST_API_ID = process.env.DUMMY_REST_API_ID || ''; const DUMMY_REST_API_RESOURCE_ID = process.env.DUMMY_REST_API_RESOURCE_ID || ''; beforeAll(async () => { //hogeFuncのConfigurationを取得 const hogeFuncConfiguration = await lambdaClient.send( new GetFunctionConfigurationCommand({ FunctionName: 'hogeFunc', }) ); //hogeFuncの環境変数をローカルの環境変数にバックアップ if ( hogeFuncConfiguration.Environment && hogeFuncConfiguration.Environment.Variables ) { process.env.HOGEFUNC_ORIGINAL_VARIABLES = JSON.stringify( hogeFuncConfiguration.Environment.Variables ); } else { throw new Error('Failed to retrieve hogeFunc enviromnet variables.'); } }); afterAll(async () => { //ローカルの環境変数からhogeFuncの環境変数をリストア const HOGEFUNC_ORIGINAL_VARIABLES = process.env.HOGEFUNC_ORIGINAL_VARIABLES; if (HOGEFUNC_ORIGINAL_VARIABLES) { await lambdaClient.send( new UpdateFunctionConfigurationCommand({ FunctionName: 'hogeFunc', Environment: { Variables: JSON.parse(HOGEFUNC_ORIGINAL_VARIABLES) }, }) ); } else { throw new Error('Failed to restore hogeFunc enviromnet variables.'); } }); test('hogeFunc test', async () => { //hogeFuncの環境変数HOGE_ENVにテスト用の値を設定 const HOGEFUNC_ORIGINAL_VARIABLES = process.env.HOGEFUNC_ORIGINAL_VARIABLES || ''; const HOGE_API_ENDPOINT = `https://${DUMMY_REST_API_ID}.execute-api.ap-northeast-1.amazonaws.com/v1/data`; await lambdaClient.send( new UpdateFunctionConfigurationCommand({ FunctionName: 'hogeFunc', Environment: { Variables: { ...JSON.parse(HOGEFUNC_ORIGINAL_VARIABLES), HOGE_API_ENDPOINT: HOGE_API_ENDPOINT, }, }, }) ); //ダミーデータ const dummyData = { key1: 'value1', key2: 123, key3: { key4: 'value2', }, }; //REST APIのレスポンスデータを設定 await apiGatewayClient.send( new UpdateIntegrationResponseCommand({ restApiId: DUMMY_REST_API_ID, resourceId: DUMMY_REST_API_RESOURCE_ID, httpMethod: 'GET', statusCode: '200', patchOperations: [ { op: 'replace', path: '/responseTemplates/application~1json', value: JSON.stringify(dummyData), }, ], }) ); //REST APIの変更をデプロイ await apiGatewayClient.send( new CreateDeploymentCommand({ restApiId: DUMMY_REST_API_ID, stageName: 'v1', }) ); await new Promise((r) => setTimeout(r, 1000)); //反映完了まで待機 //hogeFuncをテスト実行 await lambdaClient.send( new InvokeCommand({ FunctionName: 'hogeFunc', }) ); //some test });
- UpdateFunctionConfigurationCommandでLambda関数の環境変数にダミーのREST APIのエンドポイントを設定しています。
- この環境変数は
AfterAll
の後処理によりテスト後には元の値に戻されます。このあたりは前回のエントリに詳しいので合わせてどうぞ。
- この環境変数は
- UpdateIntegrationResponseCommandでREST APIのレスポンスにダミーデータを設定しています。
patchOperations
の記法が独特で調べるのに少し苦労しました。
- CreateDeploymentCommandでREST APIの変更をデプロイしています。これを忘れると変更が反映されないので注意。
先程取得したREST APIの情報を環境変数に設定し、テストを実行します。
$ export DUMMY_REST_API_ID=<DUMMY_REST_API_ID> $ export DUMMY_REST_API_RESOURCE_ID=<DUMMY_REST_API_RESOURCE_ID> $ npx jest test/hogeFunc.test.ts PASS test/aws-cdk-app.test.ts ✓ hogeFunc test (2381 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 4.864 s, estimated 7 s Ran all test suites matching /test\/aws-cdk-app.test.ts/i.
CloudWatch LogsでLambdaのイベントログを見ると、ダミーのデータが取得されていますね!
おわりに
ダミーのデータを返すREST APIをAPI GatewayのMock Integrationで作ってテストで使ってみました。
レスポンスのデータ内容を制御しづらいAPIのテストをしたい時には、今回のようなダミーのREST APIを設ければ任意のデータを返すようにできるので便利です。
一点考慮事項として、APIを叩く時はAPIキーをヘッダーに設定してリクエストする場合がほとんどだと思います。今回の内容だとそのキーの差し替えまでは対応できていないので、次回対応をしてみようと思います。
参考
- update-integration-response — AWS CLI 1.25.78 Command Reference
- update-integration — AWS CLI 1.25.78 Command Reference
以上