Amazon Web Services ブログ
AWS CDK アプリケーションのためのインテグレーションテストの作成と実行
自動化されたインテグレーションテストはシステムコンポーネントを検証し、新しいソフトウェアリリースの信頼性を高めます。AWS にデプロイされたリソースでインテグレーションテストを実行すると、AWS Identity and Access Management (IAM) ポリシー、サービスの制約、アプリケーション設定、およびランタイムコードの検証が可能になります。AWS Cloud Development Kit (AWS CDK) を Infrastructure as Code (IaC) ツールとして活用している開発者向けに、インテグレーションテストをソフトウェアリリースに簡単に実装できるテストフレームワークが用意されています。
AWS CDK は、サポートされている複数のプログラミング言語を使用して AWS リソースを定義およびプロビジョニングするためのオープンソースフレームワークです。AWS CDK には、ユニットテストとインテグレーションテストを作成および実行するためのモジュールが含まれています。assertions モジュールを使用すると、ユニットテストを記述したり、生成された CloudFormation テンプレートに対してアサートしたりできます。CDK integ-tests モジュールはインテグレーションテスト用テストケースの定義に使用でき、CDK integ-runner と組み合わせてテストを実行することもできます。integ-rnnner はリソースのプロビジョニングと削除を自動的に処理し、いくつかのカスタマイズオプションをサポートしています。アサーション関数を使用したユニットテストは、テンプレートをデプロイする前に CloudFormation テンプレートの設定をテストするために使用され、インテグレーションテストではデプロイされたリソースでアサーションを実行します。本ブログ記事では、AWS CDK を使用してサンプルアプリケーションの自動化されたインテグレーションテストを作成する方法を紹介します。
ソリューション概要
図 1 に示すサンプルアプリケーションは、サーバーレスデータエンリッチメントアプリケーションのサンプルです。データは次のようにシステム内で処理され、エンリッチされます。
- ユーザーは Amazon Simple Notification Service (Amazon SNS) トピックにメッセージをパブリッシュします。メッセージは、AWS Key Management Service (AWS KMS) のカスタマーマネージドキーを使用して保存時に暗号化されます。
- Amazon Simple Queue Service (Amazon SQS) キューは Amazon SNS トピックをサブスクライブし、パブリッシュされたメッセージが配信されます。
- AWS Lambda は Amazon SQS キューからのメッセージを受け取り、メッセージにデータを追加します。正常に処理できないメッセージはデッドレターキューに送信されます。
- 正常にエンリッチされたメッセージは、Lambda 関数によって Amazon DynamoDB テーブルに保存されます。
このサンプルアプリケーションでは、AWS CDK のインテグレーションテストフレームワークを使用して、図 2 に示すように 1 つのメッセージの処理を検証します。テストを実行するには、以下のステップを実行するようにテストフレームワークを設定します。
- Amazon SNS トピックにメッセージをパブリッシュする。アプリケーションがメッセージを処理して DynamoDB に保存するのを待ちます。
- Amazon DynamoDB テーブルを定期的にチェックして、保存したメッセージがエンリッチされていることを確認します。
前提条件
このソリューションをデプロイするには、次の準備が必要です。
- AWS アカウント
- Node.js v16 以降と npm version 9 以降のインストール
- AWS CDK version 2.73.0 以降のインストール
- GitHub リポジトリのクローンと依存関係のインストール
- バージニア北部 (us-east-1) リージョンの AWS アカウントにおける CDK Bootstrap の実行
サンプル AWS CDK アプリケーションリポジトリの構成は次のとおりです。
- /bin : AWS CDK アプリのトップレベルの定義が含まれています。
- /lib : 上記のセクションで説明したアプリケーションを定義する、テスト対象アプリケーションのスタック定義が含まれています。
- /lib/functions: Lambda 関数のランタイムコードが含まれています。
- /integ-tests : テストケースを定義および設定するためのインテグレーションテストスタックが含まれています。
リポジトリは一般的な AWS CDK アプリケーションですが、テストケース定義用のディレクトリが 1 つ追加されている点が異なります。本ブログ記事では、/integ-tests/integ.sns-sqs-ddb.ts のインテグレーションテスト定義に焦点を当て、その作成とインテグレーションテストの実行について説明します。
インテグレーションテストの作成
インテグレーションテストでは、AWS CDK アプリケーションで期待される動作を検証する必要があります。アプリケーションのインテグレーションテストは次のように定義できます。
1. CDKIntegTestSDemoStack 定義からテスト対象のスタックを作成し、アプリケーションにマッピングします。
// CDK App for Integration Tests
const app = new cdk.App();
// Stack under test
const stackUnderTest = new CdkIntegTestsDemoStack(app, ‘IntegrationTestStack’, {
setDestroyPolicyToAllResources: true,
description:
“This stack includes the application’s resources for integration testing.”,
});
2. テストケースを使用してインテグレーションテストのコンストラクトを定義します。このコンストラクトにより、integ-runner ツールの動作をカスタマイズできます。たとえば、テスト実行後に integ-runner に強制的にリソースを削除させて、強制的にクリーンアップできます。
// Initialize Integ Test construct
const integ = new IntegTest(app, ‘DataFlowTest’, {
testCases: [stackUnderTest], // Define a list of cases for this test
cdkCommandOptions: {
// Customize the integ-runner parameters
destroy: {
args: {
force: true,
},
},
},
regions: [stackUnderTest.region],
});
3. アサーションを追加してテスト結果を検証します。この例では、Amazon SNS トピックから Amazon DynamoDB テーブルへの単一のメッセージフローを検証します。アサーションは AwsApiCall メソッドを使用してメッセージオブジェクトを Amazon SNS トピックにパブリッシュします。バックグラウンドでは、このメソッドは Lambda-backed CloudFormation カスタムリソースによって、AWS SDK for JavaScript で Amazon SNS Publish API コールを実行します。
/**
* Assertion:
* The application should handle single message and write the enriched item to the DynamoDB table.
*/
const id = 'test-id-1';
const message = 'This message should be validated';
/**
* Publish a message to the SNS topic.
* Note - SNS topic ARN is a member variable of the
* application stack for testing purposes.
*/
const assertion = integ.assertions
.awsApiCall('SNS', 'publish', {
TopicArn: stackUnderTest.topicArn,
Message: JSON.stringify({
id: id,
message: message,
}),
})
4. next ヘルパーメソッドを使用して API 呼び出しをチェーンします。この例では、2 つ目の Amazon DynamoDB GetItem API コールで、プライマリキーがメッセージ ID と等しい項目が取得されますが、2 つ目の API コールの結果は、データエンリッチメントの結果として追加された属性を含むメッセージオブジェクトと一致することを期待しています。
/**
* Validate that the DynamoDB table contains the enriched message.
*/
.next(
integ.assertions
.awsApiCall('DynamoDB', 'getItem', {
TableName: stackUnderTest.tableName,
Key: { id: { S: id } },
})
/**
* Expect the enriched message to be returned.
*/
.expect(
ExpectedResult.objectLike({
Item: { id: { S: id, },
message: { S: message, },
additionalAttr: { S: 'enriched', },
},
}),
5. メッセージがアプリケーションを通過するまでにはしばらく時間がかかる場合があるため、waitForAssertions メソッドを呼び出してアサーションを非同期で実行します。つまり、Amazon DynamoDB GetItem API コールは、期待される結果が得られるか、タイムアウトに達するまで、間隔を置いて呼び出されます。
/**
* Timeout and interval check for assertion to be true.
* Note - Data may take some time to arrive in DynamoDB.
* Iteratively executes API call at specified interval.
*/
.waitForAssertions({
totalTimeout: Duration.seconds(25),
interval: Duration.seconds(3),
}),
);
6. AwsApiCall メソッドは、両方の API 呼び出しに対する適切な IAM アクセス権限を AWS Lambda 関数に自動的に追加します。サンプルアプリケーションの Amazon SNS トピックは AWS KMS キーを使用して暗号化されているため、メッセージをパブリッシュするには追加の権限が必要です。
// Add the required permissions to the api call
assertion.provider.addToRolePolicy({
Effect: 'Allow',
Action: [
'kms:Encrypt',
'kms:ReEncrypt*',
'kms:GenerateDataKey*',
'kms:Decrypt',
],
Resource: [stackUnderTest.kmsKeyArn],
});
本ブログ記事のコード全文は、この GitHub プロジェクトにあります
インテグレーションテストの実行
このセクションでは、導入したサンプルアプリケーションのインテグレーションテストを integ-runner を使用して実行し、アサーション結果を報告する方法を示します。
まず、プロジェクトの依存関係をインストールしてビルドします。
npm install
npm run build
以下のコマンドを実行し、オプションを用いたテストケースの実行を開始します。
npm run integ-test
directory オプションは、integ-runner がテスト定義ファイルを再帰的に検索する場所を指定します。parallel-regions オプションを使うと、テストを実行するリージョンのリストを定義できます。このオプションを us-east-1 に設定し、AWS CDK のブートストラップが以前にこのリージョンで実行されていることを確認します。update-on-failed オプションを使用すると、スナップショットが失敗した場合にインテグレーションテストを再実行できます。使用可能なオプションの全リストは、integ-runner の GitHub リポジトリにあります。
ヒント:開発中にデバッグ用にテストスタックを残しておきたい場合は、no-clean オプションを指定してテスト実行後もテストスタックを保持できます。
integ-runner は最初にインテグレーションテストのスナップショットをチェックして、前回の実行以降に変更がないかどうかを確認します。初回実行時には以前のスナップショットがないため、スナップショットの検証は失敗します。その結果、integ-runner は一時的なテストスタックを使用してインテグレーションテストの実行を開始し、結果を表示します。
Verifying integration test snapshots...
NEW integ.sns-sqs-ddb 2.863s
Snapshot Results:
Tests: 1 failed, 1 total
Running integration tests for failed tests...
Running in parallel across regions: us-east-1
Running test <your-path>/cdk-integ-tests-demo/integ-tests/integ.sns-sqs-ddb.js in us-east-1
SUCCESS integ.sns-sqs-ddb-DemoTest/DefaultTest 587.295s
AssertionResultsAwsApiCallDynamoDBgetItem - success
Test Results:
Tests: 1 passed, 1 total
Integ-runner は、図 3 に示すように 2 つの AWS CloudFormation スタックを生成します。IntegrationTestStack スタックには、テスト対象のスタックを表す独立したアプリケーションとして機能するサンプルアプリケーションのリソースが含まれています。DataFlowDefaultTestDeployAssert スタックには、図 4 に示すように、インテグレーションテストの実行に必要なリソースが含まれています。
クリーンアップ
指定された RemovalPolicy に基づいて、スタックが削除されるとリソースは自動的に削除されます。Amazon DynamoDB テーブルなどの一部のリソースでは、AWS CDK ではデフォルトの削除ポリシーが Retain に設定されています。インテグレーションテストリソースの削除ポリシーを Destroy に設定するには Aspects を活用します。
/**
* Aspect for setting all removal policies to DESTROY
*/
class ApplyDestroyPolicyAspect implements cdk.IAspect {
public visit(node: IConstruct): void {
if (node instanceof CfnResource) {
node.applyRemovalPolicy(cdk.RemovalPolicy.DESTROY);
}
}
}
integ-runner CLI オプションの一部として no-clean オプションを設定した場合は、スタックを手動で削除する必要があります。この操作は AWS マネジメントコンソールから、図 5 に示すように AWS CloudFormation を介して、または以下のコマンドで実行できます。
cdk destroy --all
コードリポジトリのビルドファイルをクリーンアップするには、次のスクリプトを実行します。
npm run clean
結論
AWS CDK integ-tests モジュールは、AWS CDK アプリケーションの自動化されたインテグレーションテストを定義して実施するための素晴らしいツールです。本ブログ記事では、AWS CDK インテグレーションテストを使用して、AWS にデプロイしたときに期待されるアプリケーションの動作を検証する方法を示す、実践的なコード例を紹介しました。本ブログ記事の手法を活用して独自の AWS CDK インテグレーションテストを作成し、アプリケーションリリースの品質と信頼性を向上できます。
モジュールを使い始める方法については、ドキュメントを参照してください。
読者へのお願い
integ-runner と integ-tests のモジュールは実験的なモジュールであり、変更される可能性があります。安定版モジュールと実験版モジュールの両方のリリースノートは AWS CDK GitHub リリースノートから確認できます。いつもながら、aws-cdk GitHub リポジトリへのバグレポート、機能リクエスト、プルリクエストを歓迎します。皆さんのフィードバックに基づいて、ご紹介したアルファ版のモジュールをさらに改善できます。
本記事は、Iris Kraja、Svenja Raether、Ahmed Bakry、Philip Chen による “How to write and execute integration tests for AWS CDK applications” を翻訳したものです。
翻訳はソリューションアーキテクトの山崎 宏紀が担当しました。