AWS IATKを試してみる
こんにちはryoです。
今日は新しくPublic PreviewになったAWS IATKを試してみたので紹介しようと思います。
AWS IATKとは?
AWS IATKは2023年11月16日に発表されたAWS環境で構築したアプリケーションのテストを記述するためのライブラリです。
https://aws.amazon.com/jp/about-aws/whats-new/2023/11/aws-integrated-application-test-kit-preview/
イベント駆動アプリケーションのテストに特化した機能がいくつか盛り込まれており、特にEventBridge周りのテストに必要な関数が用意されています。
またPublic Preview状態なのでAPIインターフェースに破壊的変更が入る可能性があります。
本番利用にはご注意ください。
AWS IATKの基礎概念
IATKのドキュメントには以下の3つがキーコンセプトとして紹介されています。
これらの用語はドキュメントでも多用されているため押さえておく必要があります。
- System Under Test (SUT)
- Test Harness
- Arrange, Act, Assert Testing Pattern
https://awslabs.github.io/aws-iatk/#concepts
System Under Test (SUT)
テスト対象のシステムを表します。
Test Harness
テストのために利用するAWSリソースを表します。テスト対象のAWSリソースではないので注意してください。
これらのリソースはテスト開始時に作成され、テストの終了時に破棄されます。
Arrange, Act, Assert Testing Pattern
こちらはよくAAAパターンと呼ばれているテストの実行パターンを指します。
Arrangeはテストの準備、Actでテスト対象の実行、Assertで期待値の検証を表します。
実際に試してみる
ドキュメントにはいくつかサンプルが用意されています。
例えば、こちらサンプルはAPI GatewayとLambdaで構築したRest APIがイベントのパブリッシャーとなり、別のLambdaがコンシューマーとなりイベントを利用した処理を行います。
https://awslabs.github.io/aws-iatk/tutorial/examples/eb_testing/
今回は上記のシステムの簡略化を行い、EventBridgeのみを作成します。
テスト内容としてはカスタムイベントバスにイベントの送信を行い、正しくイベントが送信出来ているか検証を行います。
テスト対象のシステムとして、EventBridgeのカスタムイベントバスにイベントを送信し、後段でイベントに対して処理を行うものと想定します。
この他にもX-ray利用時のテストサンプルなども記載されているのでぜひご参照ください。
テスト対象のリソース
今回は簡略化のためEventBridgeのみをデプロイします(今回はイベントのコンシューマーは含まない)。
テスト対象のリソースとして、EventBridgeのカスタムイベントバスとイベントルールを用意します。
AWS CDKを利用して構築していきます。またCDK自体のデプロイ方法などは省略します。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import * as event from "aws-cdk-lib/aws-events";
export class AwsIatkTestStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// イベントバスの作成
const eventBus = new event.EventBus(this, "TestEventBus");
new cdk.CfnOutput(this, "EventBusName", {
value: eventBus.eventBusName,
});
// イベントルールの作成
const eventRule = new event.Rule(this, "TestEventRule", {
eventBus: eventBus,
eventPattern: {
source: ["com.test"],
detailType: ["Test"],
},
});
new cdk.CfnOutput(this, "EventRuleName", {
value: eventRule.ruleName,
});
}
}
このスタックでは、それぞれのリソースを作成し、イベントバス名とイベントルール名をCloudFormationのOutputとして登録します。
テストコードを書く
事前準備
AWS IATKは現在Pythonのライブラリのみを提供しているので、Pythonでテストコードを記述していきます。
今回はpipを利用してライブラリのインストールを行なっています。venv等の仮想環境はお好みで利用してください。
まずはrequirements.txt
を用意し以下の内容で保存をします。
aws-iatk==0.1.0
boto3==1.29.3
boto3-stubs[events]==1.29.3
pytest==7.4.3
テストランナーとしてはunittest
とpytest
のどちらも利用出来ますが、この記事ではpytest
を利用します。
保存が出来たら以下のコマンドでインストールを行います。
pip install -r requirements.txt
aws-iatkライブラリにおける認証情報の扱いなどはドキュメントに詳しく載っているので参照ください。
https://awslabs.github.io/aws-iatk/
簡単なテストから書いてみる
まずは簡単なテストから書いてみます。
このテストではCloudFormationのOutputから値を取得し、正しい値が取得出来ているかを確認しています。
from typing import Dict
import pytest
from aws_iatk import AwsIatk
stack_name = "AwsIatkTestStack"
region = "ap-northeast-1"
iatk = AwsIatk(region=region)
@pytest.fixture(scope="module")
def stack_output() -> Dict[str, str]:
outputs = iatk.get_stack_outputs(
stack_name=stack_name,
output_names=["EventBusName", "EventRuleName"],
).outputs
return outputs
def test_stack_output(stack_output: Dict[str, str]) -> None:
assert stack_output["EventBusName"] == "イベントバス名"
get_stack_outputs
メソッドを利用することでCloudFormationのOutputから値を取得し、Dictionary型に変換して返してくれます。
https://awslabs.github.io/aws-iatk/api/python/aws_iatk.html#AwsIatk.get_stack_outputs
このコードの通りあくまでテストコードの実行はpytest
で行うため、pytest
を書いたことがある方であればすんなり書き始められると思います。
テストコードの追加
先ほどのテストコードを修正して、以下のように書き換えます。
このテストコードでは最初に書いた通り、カスタムイベントバスに対してイベントを送信し正しくイベントを送信出来ているか検証します。
import json
from typing import Dict, Generator, TypedDict
import boto3
import pytest
from aws_iatk import AwsIatk
class Input(TypedDict):
listener_id: str
event_bus_name: str
event_rule_name: str
stack_name = "AwsIatkTestStack"
region = "ap-northeast-1"
iatk = AwsIatk(region=region)
boto3_client = boto3.client("events", region_name=region)
@pytest.fixture(scope="module")
def stack_output() -> Dict[str, str]:
# CloudFormationのOutputから値を取得
outputs = iatk.get_stack_outputs(
stack_name=stack_name,
output_names=["EventBusName", "EventRuleName"],
).outputs
return outputs
@pytest.fixture(scope="function", autouse=True)
def setup_teardown(stack_output: Dict[str, str]) -> Generator[Input, None, None]:
# Before test
listener = iatk.add_listener(
event_bus_name=stack_output["EventBusName"],
rule_name=stack_output["EventRuleName"].split("|")[1],
)
yield {
"listener_id": listener.id,
"event_bus_name": stack_output["EventBusName"],
# 'EventBus|EventRule'という形式で出力されるのでsplitする
"event_rule_name": stack_output["EventRuleName"].split("|")[1],
}
# After test
iatk.remove_listeners(ids=[listener.id])
def test_event_polling(setup_teardown: Input) -> None:
# EventBridgeにイベントを送信
put_events_res = boto3_client.put_events(
Entries=[
{
"EventBusName": setup_teardown["event_bus_name"],
"DetailType": "Test",
"Source": "com.test",
"Detail": "{}",
},
],
)
assert put_events_res["FailedEntryCount"] == 0
# 送信したイベントを取得
received_events = iatk.poll_events(
listener_id=setup_teardown["listener_id"],
wait_time_seconds=10,
max_number_of_messages=1,
).events
assert len(received_events) == 1
event = json.loads(received_events[0])
assert event["detail-type"] == "Test"
assert event["source"] == "com.test"
以下で関数ごとに説明を行います。
@pytest.fixture(scope="function", autouse=True)
def setup_teardown(stack_output: Dict[str, str]) -> Generator[Input, None, None]:
# Before test
listener = iatk.add_listener(
event_bus_name=stack_output["EventBusName"],
rule_name=stack_output["EventRuleName"].split("|")[1],
)
yield {
"listener_id": listener.id,
"event_bus_name": stack_output["EventBusName"],
# 'EventBus|EventRule'という形式で出力されるのでsplitする
"event_rule_name": stack_output["EventRuleName"].split("|")[1],
}
# After test
iatk.remove_listeners(ids=[listener.id])
まずこちらの関数ではpytest
のfixture
機能を利用してテストの前後で処理を実行しています。
add_listener
メソッドを呼び出すとテストに必要なAWSリソースが作成されます。
このメソッドではテスト用のイベントルールとイベントの送信先であるSQSが作成されています。
テスト用に作成されるイベントルールのイベントパターンはadd_listener
メソッドに渡したイベントルールと同じものになります。
リソース同士の関係を図に表すと以下のようになります。
イベントを取得する際は、こちらのSQSからメッセージの取得を行っているようです。
テスト終了時にはremove_listeners
メソッドを呼び出しこれらのリソースの削除を行います。
def test_event_polling(setup_teardown: Input) -> None:
# EventBridgeにイベントを送信
put_events_res = boto3_client.put_events(
Entries=[
{
"EventBusName": setup_teardown["event_bus_name"],
"DetailType": "Test",
"Source": "com.test",
"Detail": "{}",
},
],
)
assert put_events_res["FailedEntryCount"] == 0
# 送信したイベントを取得
received_events = iatk.poll_events(
listener_id=setup_teardown["listener_id"],
wait_time_seconds=10,
max_number_of_messages=1,
).events
assert len(received_events) == 1
event = json.loads(received_events[0])
assert event["detail-type"] == "Test"
assert event["source"] == "com.test"
こちらの関数はテストケースを表しています。
まずはboto3
を利用してカスタムイベントバスにイベントを送信します(Arrange)。
次にpoll_events
メソッドを利用することで送信したイベントが対象のイベントルールで正しく処理できているかを確認します。
このメソッドは先ほど作成したSQSからメッセージを取得し返却します。
他にはwait_until_event_matched
という取得とAssertionを行うメソッドも用意されています。
https://awslabs.github.io/aws-iatk/api/python/aws_iatk.html#AwsIatk.wait_until_event_matched
最後のAssertionでは取得したイベントに対して期待したイベントかどうかを検証します。
ここではメッセージ数やメッセージ内容の簡単な検証を行います。
まとめ
この記事では新しく発表されたAWS IATKの紹介を行いました。
今回のテストケースはかなり単純ですが、IATKを利用することでイベント駆動システムのテスト自動化を楽に実装することが可能です。
APIリファレンスには必要となるIAM権限も記載されているので、どのようなメソッドがあるかぜひご確認ください。
https://awslabs.github.io/aws-iatk/api/python/aws_iatk.html
以上、参考になれば幸いです。