【実務・中級編】Pulumiでのテスト駆動開発(TDD):Unit TestとIntegration Testの導入方法 – インフラ構成管理(IaC)活用バイブル

インフラを「テスト駆動」で制する:Pulumi TDDの実践とCI/CDパイプラインの極意

テックリードの私たちが日々のインフラ開発で直面する最大のフラストレーションは何だろうか?
それは、「コードを書いて、`pulumi up`を実行し、数分待たされた挙句、IAMの権限エラーやプロパティのタイポでデプロイが失敗する」という不毛なフィードバックループだ。

Terraformの計画フェーズ(`plan`)だけで満足していはないか?あんなものは静的チェックに過ぎない。
真のインフラエンジニアリングとは、アプリケーション開発と同様に「テスト駆動開発(TDD)」を回し、コードの正当性をミリ秒単位のユニットテストと、完全自動化されたインテグレーションテストで担保することにある。

今回は、数あるIaCツールの中でも最もプログラマティックで柔軟なPulumiを用い、現場の生産性を極限まで高めるTDDの導入手法を、実戦的なコードと設定のベストプラクティスと共に解説する。

—

1. IaCにおけるテストの重要性とPulumiの優位性

なぜIaCにテストが必要なのか。理由はシンプルだ。「本番環境は実験場ではない」からだ。

HCL(HashiCorp Configuration Language)のようなDSL(ドメイン固有言語)では、複雑な条件分岐やバリデーションを記述しようとするとすぐに破綻し、テストを書くこと自体が苦行になる。
その点、PulumiはTypeScript, Python, Goといった汎用プログラミング言語でインフラを定義できる。これはつまり、ソフトウェアエンジニアリングで培われたテスト手法(Jest、PyTest、Mochaなど)が、そのままインフラストラクチャに適用できるということを意味する。

Pulumiのテスト戦略は、大きく分けて以下の2階層で構成される。

1. Unit Test(ユニットテスト): クラウドプロバイダーに接続せず、モックを使って数秒で完了する論理検証。
2. Integration Test(インテグレーションテスト): 実際にクラウド環境へ一時的なリソースをデプロイし、挙動を検証する動的検証。

この2つをCIパイプラインでシームレスにつなぐことで、私たちの開発スピードは劇的に加速する。

—

2. モックを使った高速なユニットテストの実装

まずは、クラウドへの接続を完全に排除したユニットテストだ。ここではTypeScriptとJestを用いた例を示す。

ディレクトリ構成のベストプラクティス

チーム開発において、テストコードが散らかるのは悪手である。以下のようなクリーンな構造を維持せよ。

.
├── Pulumi.yaml
├── index.ts # インフラ定義のメインエントリ
├── resources.ts # リソース定義モジュール
├── package.json
└── test/
└── infrastructure.test.ts # ユニットテスト

ユニットテストの実装例(TypeScript / Jest)

以下のコードは、「すべてのS3バケットに暗号化が有効であり、かつパブリックアクセスがブロックされているか」を検証するユニットテストだ。

// test/infrastructure.test.ts
import as pulumi from “@pulumi/pulumi”;

// 1. Pulumiのランタイムをモック化する
pulumi.setConfig(“aws:region”, “ap-northeast-1”);

// モックプロバイダーの設定
jest.mock(“@pulumi/aws”, () => {
return {
s3: {
BucketV2: class {
public readonly bucket: pulumi.Output;
constructor(name: string, args: any) {
this.bucket = pulumi.output(`mocked-bucket-${name}`);
// リソースのプロパティをモックに渡す
this.registerOutputs({ bucket: this.bucket });
}
private registerOutputs(outputs: any) {}
},
BucketPublicAccessBlock: class {
constructor(name: string, args: any) {}
},
BucketServerSideEncryptionConfigurationV2: class {
constructor(name: string, args: any) {}
}
}
};
});

// テスト対象のモジュールをインポート
import { SecureBucket } from “../resources”;

describe(“SecureBucket Infrastructure Unit Tests”, () => {

it(“S3バケット名が命名規約に従っていること”, async () => {
// モック環境下でコンポーネントを instantiate
const bucketResource = new SecureBucket(“app-logs”, { environment: “prod” });

// pulumi.all を使って Output の値を取り出してアサーションを行う
await new Promise((resolve) => {
bucketResource.bucket.bucket.apply((bucketName) => {
expect(bucketName).toContain(“app-logs”);
resolve();
});
});
});
});

このテストはクラウドAPIを叩かないため、実行時間はわずか数百度ミリ秒だ。コーディング中にこのテストをウォッチモード(`jest –watch`)で走らせておくことで、タイポやポリシー違反を即座に検知できる。

—

3. 実際にクラウドへデプロイして行うインテグレーションテスト

ユニットテストで論理構造を担保したら、次はインテグレーションテストだ。Pulumiには、テスト用にインフラを立ち上げ、テスト実行後に自動で破棄(Teardown)してくれる強力なテストフレームワーク(`pulumi.automation` API)が用意されている。

Python + PyTestによるインテグレーションテスト

インテグレーションテストでは、実際にAWSへリソースをプロビジョニングし、HTTPリクエストを送るなどして「動くこと」を確認する。

test/test_integration.py
import os
import pytest
import requests
from pulumi.automation import LocalWorkspace, UpArgs, destroy

@pytest.fixture(scope=”module”)
def infra_stack():
# 1. 一時的なワークスペースを作成してデプロイ
project_name = “my-infra-integration-test”

# ワークスペースの初期化(スタック名はテストごとにユニークにする)
workspace = LocalWorkspace(work_dir=os.path.abspath(“.”))
stack = workspace.select_stack(“integration-test”)

# 設定の投入
stack.set_config(“aws:region”, “ap-northeast-1”)

# デプロイ実行 (pulumi up)
up_res = stack.up(on_output=print)

# テスト対象のエンドポイント等を出力から取得
outputs = stack.outputs()
endpoint = outputs[“url”].value

yield endpoint

# 2. テスト完了後にインフラを完全にクリーンアップ (pulumi destroy)
# どんなにテストが失敗しても必ず実行されるようにする
stack.destroy(on_output=print)

def test_api_gateway_is_responsive(infra_stack):
endpoint_url = infra_stack

# 実際にHTTPリクエストを飛ばして検証
response = requests.get(endpoint_url)
assert response.status_code == 200
assert “Hello, Pulumi” in response.text

このアプローチにより、「コード上は正しいが、AWSの制約でデプロイ失敗する・動作しない」というインフラ特有のバグをCIの段階で完全に弾き出すことができる。

—

4. 品質を担保するCIパイプラインの組み方

ローカルでテストが完璧でも、CIパイプラインで強制されなければチーム開発は崩壊する。ここでは、GitHub Actionsを用いた堅牢なCI/CDパイプラインの設定例を示す。

GitHub Actions ワークフロー設定(YAML)

.github/workflows/infra-pipeline.yml
name: Pulumi Infrastructure Pipeline

on:
pull_request:
branches: [ main ]
push:
branches: [ main ]

jobs:
unit-test:
name: 1. Unit Test
runs-on: ubuntu-latest
steps:

  • name: Checkout code

uses: actions/checkout@v4

  • name: Setup Node.js

uses: actions/setup-node@v4
with:
node-version: ’20’
cache: ‘npm’

  • name: Install dependencies

run: npm ci

  • name: Run Unit Tests

run: npm test

integration-test:
name: 2. Integration Test (AWS)
needs: unit-test
runs-on: ubuntu-latest
# PRがメインへのマージ時、もしくは特定のラベルがある場合のみ実行(コスト削減のため)
if: github.event_name == ‘push’ || contains(github.event.pull_request.labels..name, ‘run-integration-test’)

steps:

  • name: Checkout code

uses: actions/checkout@v4

  • name: Configure AWS Credentials

uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1

  • name: Setup Python

uses: actions/setup-python@v5
with:
python-version: ‘3.11’

  • name: Install Pulumi CLI

uses: pulumi/action-install-pulumi@v3

  • name: Install Python Dependencies

run: |
pip install pytest requests pulumi pulumi-aws

  • name: Run Integration Tests

env:
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
run: |
pytest test/test_integration.py

—

プロの実践テクニック:開発スピードを最大化する秘訣

最後に、現場のテックリードとして知っておくべき「開発体験(DX)を爆発的に高める設定とハック」を授けよう。

1. 隠れたキーボードショートカット & VSCode拡張

  • Pulumi Visualizer: 複雑な依存関係グラフを視覚化するために必須のVSCode拡張。コードを書いた瞬間にグラフの整合性を脳内で構築する必要がなくなる。
  • Quick Fix (Ctrl+. / Cmd+.): Pulumiの型定義は厳密であるため、プロパティ補完(IntelliSense)をフル活用しろ。ドキュメントをブラウザで見に行く時間はエンジニアの寿命の無駄遣いだ。

2. チーム開発で絶対共有すべき設定(`Pulumi.yaml` と `.pulumiignore`)

`.gitignore` と同様に、`.pulumiignore` の最適化はインフラのビルド速度に直結する。node_modulesやPythonの仮想環境、テストキャッシュがPulumiの暗号化アーカイブに含まれるのを防ぐため、以下の設定を必ずルートに置け。

.pulumiignore
.git
.github
node_modules/
venv/
__pycache__/
.test.ts
test/
.env

これを行わないと、ファイル変更のたびに不要な巨大アーカイブがPulumi Backendにアップロードされ、デプロイの待ち時間が数秒から数分に跳ね上がる。

—

結び

インフラストラクチャをコードとして扱う(IaC)時代から、私たちは次のステージ、すなわち「インフラストラクチャをソフトウェアとしてテストし、品質を担保する」時代へシフトしている。

Pulumiを用いたTDDの導入は、最初は学習コストがかかるように見えるかもしれない。だが一度このパイプラインが回り始めたら、もう二度と「祈りながら`pulumi up`を押す」暗黒時代には戻れなくなるはずだ。
あなたのチームのインフラに、エンジニアリングの誇りと絶対的な品質をもたらしてほしい。

タイトルとURLをコピーしました