AWS-CodeBuild

Python版AWS CDKでCodeBuildでテストを自動化する方法

はじめに

こんにちは、omkです。
AWS CDKでデプロイ前にいい感じにテストしたかったのでいい感じにテストしてみました。

前提

Python版のCDK(v2)を利用します。
テストの実施はpytestを利用します。

CDKでのテストの方法はAWS公式ブログあらゆる言語でのCDKアプリケーションのテストが非常にわかりやすいです。

CDKでは「きめ細かなアサーション(パラメータが期待するものになっているか)」と「スナップショットテスト(テンプレートに変更がないか)」の2パターンが標準で用意されていますが、今回は前者の内容を実施します。

環境構築

CDKのデフォルトの構造は以下のようになっており、テストコードは「{プロジェクトのルート}/tests/unit/」配下に設置することでpytestで単体テストができます。

.
├── README.md
├── app.py
├── cdk.json
├── sample
│   ├── __init__.py
│   └── sample_stack.py
├── requirements-dev.txt
├── requirements.txt
├── source.bat
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_sample_stack.py

pytestのインストールが必要なので実施します。
デフォルトでrequirements-dev.txtに記載があります。

pytest==6.2.5

必要なライブラリはここに追記していきます。
で、以下のコマンドで一括でインストールします。

pip install -r requirements-dev.txt

これでテストできる環境ができました。
次はテストコードを作成していきます。

テストコード作成

CDKのアサーションは「aws_cdk.assertions」で実装されています。

assertionsとテスト対象となるスタックをインポートします。

import aws_cdk.assertions as assertions
from sample.sample_stack import DynamoTableStack

スタックを定義してテンプレートを取得します。

app = cdk.App()
DynamoStack = DynamoTableStack(app, "table-stack")
template = assertions.Template.from_stack(DynamoStack)

あとはこのテンプレートに対して必要なテスト内容を設定していきます。

リソースのプロパティをテストします。

has_resource_propertiesはスタック中の対応したリソースが1つでもあれば合格。

def test_dynamodb_pitr():
    template.has_resource_properties(
        "AWS::DynamoDB::Table",
        {
            "PointInTimeRecoverySpecification" : {
                "PointInTimeRecoveryEnabled" : True
            }
        },
    )

all_resources_propertiesはすべてのリソースが対応している必要あり。セキュリティポリシーのチェックとかはこっちでやることが多そうですね。

def test_dynamodb_pitr():
    template.all_resources_properties(
        "AWS::DynamoDB::Table",
        {
            "PointInTimeRecoverySpecification" : {
                "PointInTimeRecoveryEnabled" : True
            }
        },
    )

失敗時にはちゃんと個数も出してくれます。
▼すべて失敗時

@jsii/kernel.RuntimeError: Error: Template has 3 resource(s) with type AWS::DynamoDB::Table, but none match as expected.

▼1個だけ成功(それ以外失敗)

@jsii/kernel.RuntimeError: Error: Template has 3 resource(s) with type AWS::DynamoDB::Table, but only 1 match as expected.

all_resourcesでリソースプロパティに含まれない項目もテストできます。

def test_dynamodb_deletion():
    template.all_resources(
        "AWS::DynamoDB::Table", {
            "DeletionPolicy": "Retain"
        }
    )

他にも色々テスト方法があるのでリファレンスを参照してみてください。

テスト実行

pytestでのテストの実行は以下のようなコマンドで実行できます。

python -m pytest --junitxml=./pytest_results.xml

「--junitxml」でjunitxml形式で出力するよう指定していますが、結果の形式にも色々あるみたいです。

テストのレポート

CodeBuildではpytestでのテスト結果をレポートというかたちで出力できます。
Buildspecのreportsに結果のファイルを指定することでレポートとして読み込んでくれます。
対応しているレポートの形式は以下に記載があります。
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/test-reporting.html

以下のような感じでbuildspecを用意しておくとCDKのテストを自動化できて良いです。

phases:
  install:
    on-failure: ABORT
    commands:
      - pip3 install -r requirements-dev.txt

  build:
    commands:
      - python -m pytest --junitxml=./pytest_results.xml
reports:
 pytest-group:
   files:
     - pytest_results.xml
   base-directory: './'
   file-format: JUNITXML

余談ですが、CodeBuildでテストに失敗するとレポートが確認できない問題が生じました。
ドキュメントに以下の記載があります。
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/report-create.html

build または post_build シーケンスの commands セクションで、レポートグループに対して指定したテストケースを実行するコマンドを指定します。詳細については、「 テストコマンドの指定 」を参照してください。

どこでテストするべきか把握していなかったのですが、pre_buildでテストしようとしていたので問題でした。

CodeBuildのビルドフェーズの移行について以下のドキュメントに記載があります。
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/view-build-details.html#view-build-details-phases

成功・失敗時の動きがフェーズによって異なるんですね。

PRE_BUILDフェーズでは成功時に次のBUILDフェーズに移行し、失敗時にはフェーズを飛ばしてFINALIZINGフェーズに移行します。
一方でBUILDフェーズ以降では成功時も失敗時も次のフェーズに移行するという違いがあります。

おそらく、UPLOAD_ARTIFACTSフェーズあたりでテスト結果がアップロードされていて、PRE_BUILD以前では失敗時にスキップされるのでテスト失敗(不合格)時に結果が上がってこないようになっていたのだと思います。

結果確認

結果はこのような形で確認できます。
テストケースごとに成功失敗が分かりますね。

テストケースを選択することで具体的なエラー文が確認できます。

レポートはビルドの結果から確認できます。

まとめ

CodeBuildでCDKのテストを自動化する方法をまとめました。
ここで作成したBuildプロジェクトをパイプラインに組み込むことで簡単に作成したCDKプロジェクトをテストできるのでCI/CDが爆速になります。
テストも含めてテンプレートを作成できるのがCDKの良いところですね。

それでは皆さま良いCDKライフを!!

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA