LocalStack から公式に提供されている localstack-utils を使うと,pytest など Python で単体テストを実行するときに一時的な(使い捨て可能な)LocalStack 環境を起動できる🌍
ちなみに僕は普段仕事で testcontainers-python の LocalStackContainer を使ってて最高に便利なんだけど,結果的に localstack-utils もほとんど同じように使うことができた👌
現在最新は localstack-utils 1.0.1 だった.
サンプルコード
👾 app.py
まずは Amazon DynamoDB の Forum
テーブルからアイテムを取得する search_forum()
関数を今回のテスト対象とする.Forum
テーブルというのは Amazon DynamoDB のドキュメントに載っているサンプルでそのまま使うことにした.
そして,boto3 client は環境変数 ENV
によって3種類作れるようにしてある👌
local
: ローカル開発用 (LocalStack)test
: テスト用 (localstack-utils / LocalStack)- その他: 実際の AWS アカウント
ちなみに localstack-utils のデフォルト設定では 4566
ポートで LocalStack を起動する.それだとローカル開発用の LocalStack とポートが競合してしまうため,今回は 14566
ポートで起動することにした.
import os import boto3 TABLE_NAME = 'Forum' if os.environ['ENV'] == 'local': dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:4566') elif os.environ['ENV'] == 'test': dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:14566') else: dynamodb = boto3.client('dynamodb') def search_forum(name): return dynamodb.get_item( TableName=TABLE_NAME, Key={ 'Name': {'S': name}, }, )
👾 test_app.py
pytest 実行時に呼び出すフィクスチャとして @pytest.fixture
デコレータで _setup()
関数を実装した.localstack-utils には LocalStack を起動する startup_localstack()
関数と LocalStack を停止する stop_localstack()
関数が実装されているため,それを _setup()
関数内で実行している.14566
ポートで起動する設定もしてある👌
そして,Amazon DynamoDB テーブル Forum
を作って,サンプルデータ(アイテム)を1つ登録している.最後にテストケースとしては search_forum()
関数を呼び出して「アイテムを取得できる場合」と「アイテムを取得できない場合」を確認している✔️
import boto3 import pytest from app import search_forum from localstack_utils.localstack import startup_localstack, stop_localstack TABLE_NAME = 'Forum' @pytest.fixture(scope='module', autouse=True) def _setup(): startup_localstack(gateway_listen='0.0.0.0:14566') dynamodb = boto3.client('dynamodb', endpoint_url='http://localhost:14566') dynamodb.create_table( TableName=TABLE_NAME, KeySchema=[ { 'AttributeName': 'Name', 'KeyType': 'HASH', } ], AttributeDefinitions=[ { 'AttributeName': 'Name', 'AttributeType': 'S', } ], BillingMode='PAY_PER_REQUEST', ) item = { 'Name': {'S': 'Amazon DynamoDB'}, 'Category': {'S': 'Amazon Web Services'}, 'Threads': {'N': '2'}, 'Messages': {'N': '4'}, 'Views': {'N': '1000'}, } dynamodb.put_item(TableName=TABLE_NAME, Item=item) yield stop_localstack() def test_search_forum(): item = search_forum('Amazon DynamoDB') assert item['Item']['Category']['S'] == 'Amazon Web Services' assert item['Item']['Views']['N'] == '1000' item = search_forum('Amazon S3') assert 'Item' not in item
動作確認
期待通り実行できた👏
$ ENV=test pytest -p no:warnings =========================================================================================== test session starts ============================================================================================ (中略) configfile: pyproject.toml collected 1 item tests/test_app.py . [100%] ============================================================================================ 1 passed in 4.97s =============================================================================================
詳しくはコード参照
localstack-utils にはドキュメントというドキュメントはなく,オプションなど詳しくは GitHub のコードを確認する必要がある.例えば今回は startup_localstack()
関数に gateway_listen
を設定したけど,他にも image_name
/ tag
/ pro
なども設定できる👌
まとめ
localstack-utils を使って Python で単体テストを実行するときに一時的な(使い捨て可能な)LocalStack 環境を活用しよう❗️