Testcontainers で単体テスト用の LocalStack コンテナを簡単に管理しよう

記事タイトルとURLをコピーする

本記事時は サーバーワークスアドベントカレンダー 2024 - シリーズ 1 の 15 日目の記事です。

LocalStack とは

こちらはご存知の方も多いかと思いますが、LocalStack は AWS の主要なサービスをローカル環境で模擬的に動作させるツールです。これにより、実際の AWS 環境を利用せずにローカルでアプリケーションの開発・テストが可能になります。

www.localstack.cloud

Testcontainers とは

Testcontainersは、コンテナを使用した自動テストを簡単に実現するためのライブラリです。テスト環境に必要なデータベースやメッセージキューなどをDockerコンテナで動的に起動し、テストが終了すると自動的にクリーンアップされます。これにより、依存関係を隔離した信頼性の高いテストが可能になります。

testcontainers.com

Thoughtworks の Technology Radar Volume 31 Languages & Frameworks でも dbt と並んで Adopt (強く推奨) に位置付けられています。

Testcontainers は Go、Java、Node.js、Python 等、11 の言語で実装されており、1 つの言語で使い方を習得してしまえば、他の言語でも同様のフローでコンテナを利用したテストを実装することができます。対応言語は以下リンクから確認してください。

testcontainers · GitHub

セットアップ

今回は Python のテストコード内で Testcontainers で LocalStack を管理してしようします。

実行環境は以下の通りです。

$ sw_vers
ProductName:            macOS
ProductVersion:         15.1.1
BuildVersion:           24B91

$ docker -h
Docker version 20.10.17, build 100c701

$ python -V
Python 3.12.4

まずは Python の仮想環境から作成します。

$ mkdir testcontainer-blog && $_
$ python -m venv .venv
$ source .venv/bin/activate

boto3、testcontainers-python、pytest をインストールします。

$ pip install boto3 testcontainers pytest

テスト対象コード

簡単ですが、以下のようなテスト対象コードを用意しました。パラメータストアから値を取得して返す関数です。引数に boto3 の SSM クライアント、パラメータ名をとります。

# main.py
  
def get_parameter_value(ssm_client, parameter_name):
    response = ssm_client.get_parameter(
        Name=parameter_name
    )
    return response['Parameter']['Value']

テストコードの実装

Testcontainers 経由で LocalStack を使用したテストコードを実装します。全体像はこのような形となります。put_parameter でテストデータを投入、テスト対象メソッド(get_parameter_value)の実行、最後にアサーションを行っています。

# test_main.py
  
from main import get_parameter_value
from testcontainers.localstack import LocalStackContainer
  
  
def test_get_parameter_value():
    with LocalStackContainer() as localstack:
  
        # arrange
        client = localstack.get_client("ssm")
        client.put_parameter(
            Name="sample",
            Type="String",
            Value="sample-value"
        )
  
        # act
        value = get_parameter_value(client, "sample")
  
        # assert
        assert value == "sample-value"

ポイントは LocalStackContainer です。コンテキストマネージャーとして利用可能で、前処理(__enter__)で LocalStack のイメージの Pull から起動、後処理(__exit__)で起動したコンテナの破棄まで行ってくれます。 また、localstack.get_client("ssm") で LocalStack 向けの設定を施した client を取得できます。

Testcontainers を利用しない場合、docker-compose.yml 等に LocalStack を定義し、起動、起動したポートをエンドポイントに持つクライアントを定義する必要があります。また、テスト状況に応じたコンテナの破棄等も自前で実装する必要があるため、かなり簡単にコンテナを使用したテストが書けることがお分かりいただけたかと思います。

サンプルでは 1 つのテストメソッド内で起動・破棄をしていますが、同様に複数メソッド実装してしまうと各テストメソッド毎にコンテナの起動・破棄を行ってしまうため、fixture 等を駆使してコンテナのライフサイクルを調整すると良いでしょう。

まとめ

Testcontainers を利用するとコンテナを使用したテストを簡単に書くことができました。また、テストコードの実装だけでなく、AWS 環境の用意なしに手元で検証コードを書くことができます。様々な言語で利用できますので、是非お試しいただければと思います。

"; doc.innerHTML = entry_notice + doc.innerHTML; }
' } }) e.innerHTML = codeBlock; });