見出し画像

AWS CDKのテストを実行してみた(python編)

検証環境の情報

OS:macOS Monterey
IDE:Visual Studio  Code

$ node --version
v14.17.6

$ cdk --version
2.40.0 (build 56ba2ab)

$ python --version
Python 3.8.13

CDKプロジェクトの作成

1.ディレクトリの作成

cdk-pythonというディレクトリを作成し、カレントを移動します

$ mkdir cdk-python && cd cdk-python

2.CDKの初期化

CDKはTypescript,Python,Java,C#,Gでプログラミングすることができます。
cdk init コマンドを実行する時に、--languageオプションで言語を指定します。

今回は、サンプルリソースを含めたCDKの初期化を行うため"sample-app"を指定しています。(後からテストコードを実行します)

サンプルリソースが不要時は"app"を指定して実行します
cdk init app --language [言語]

$ cdk init sample-app --language python
Applying project template sample-app for python

# Welcome to your CDK Python project!
:
Please run 'python3 -m venv .venv'!
Executing Creating virtualenv...
✅ All done!

// cdk init で生成されるディレクトリやファイル
$ tree
.
├── README.md
├── app.py
├── cdk.json
├── cdk_python
│   ├── __init__.py
│   └── cdk_python_stack.py
├── requirements-dev.txt
├── requirements.txt
├── source.bat
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_cdk_python_stack.py

2.python仮想環境に入ります

activateすると、プレフィックスに(.venv)が付与されます。
※抜ける場合は、deactivateを実行します

$ source .venv/bin/activate
(.venv) $

3.ライブラリのインストール

(.venv) $ pip install -r requirements.txt

app.pyの内容

#!/usr/bin/env python3

import aws_cdk as cdk

from cdk_python.cdk_python_stack import CdkPythonStack


app = cdk.App()
CdkPythonStack(app, "cdk-python")

app.synth()

cdk_python_stack.pyの内容

from constructs import Construct
from aws_cdk import (
    Duration,
    Stack,
    aws_iam as iam,
    aws_sqs as sqs,
    aws_sns as sns,
    aws_sns_subscriptions as subs,
)


class CdkPythonStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        queue = sqs.Queue(
            self, "CdkPythonQueue",
            visibility_timeout=Duration.seconds(300),
        )

        topic = sns.Topic(
            self, "CdkPythonTopic"
        )

        topic.add_subscription(subs.SqsSubscription(queue))

4.スタック名の確認

(.venv) $ cdk list
cdk-python

5.CloudFormationTemplateの確認

(.venv) $ cdk synth
Resources:
  CdkPythonQueueE793920B:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 300
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    Metadata:
      aws:cdk:path: cdk-python/CdkPythonQueue/Resource
  CdkPythonQueuePolicy645AC77E:
    Type: AWS::SQS::QueuePolicy
    Properties:
      PolicyDocument:
        Statement:
          - Action: sqs:SendMessage
            Condition:
              ArnEquals:
                aws:SourceArn:
                  Ref: CdkPythonTopic5B392E0B
            Effect: Allow
            Principal:
              Service: sns.amazonaws.com
            Resource:
              Fn::GetAtt:
                - CdkPythonQueueE793920B
                - Arn
        Version: "2012-10-17"
      Queues:
        - Ref: CdkPythonQueueE793920B
    Metadata:
      aws:cdk:path: cdk-python/CdkPythonQueue/Policy/Resource
  CdkPythonQueuecdkpythonCdkPythonTopicA9819E1D183DEAD0:
    Type: AWS::SNS::Subscription
    Properties:
      Protocol: sqs
      TopicArn:
        Ref: CdkPythonTopic5B392E0B
      Endpoint:
        Fn::GetAtt:
          - CdkPythonQueueE793920B
          - Arn
    Metadata:
      aws:cdk:path: cdk-python/CdkPythonQueue/cdkpythonCdkPythonTopicA9819E1D/Resource
  CdkPythonTopic5B392E0B:
    Type: AWS::SNS::Topic
    Metadata:
      aws:cdk:path: cdk-python/CdkPythonTopic/Resource
  CDKMetadata:
    Type: AWS::CDK::Metadata
    Properties:
      Analytics: v2:deflate64:H4sIAAAAAAAA/1WNQQ6CMBBFz8K+HVFcuOcCCO4NlBpHsIVOG0Oa3l3aJiZu5v//8pI5wbmEsug/xMU48RkH8J3txcR2dPe0Evirk06y+qFySbfRM4rtB/MMjNTud24gYXCxqFU0/vZNLygiTSWEWFtJ2hmRftRajRjNwJrNPrU6VHCBY1W8CJEbpyy+JbQ5v3NVS5++AAAA
    Metadata:
      aws:cdk:path: cdk-python/CDKMetadata/Default
    Condition: CDKMetadataAvailable
Conditions:
  CDKMetadataAvailable:
    Fn::Or:
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - af-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-northeast-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-northeast-2
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-southeast-1
          - Fn::Equals:
              - Ref: AWS::Region
              - ap-southeast-2
          - Fn::Equals:
              - Ref: AWS::Region
              - ca-central-1
          - Fn::Equals:
              - Ref: AWS::Region
              - cn-north-1
          - Fn::Equals:
              - Ref: AWS::Region
              - cn-northwest-1
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-central-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-north-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-1
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-2
          - Fn::Equals:
              - Ref: AWS::Region
              - eu-west-3
          - Fn::Equals:
              - Ref: AWS::Region
              - me-south-1
          - Fn::Equals:
              - Ref: AWS::Region
              - sa-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-east-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-east-2
      - Fn::Or:
          - Fn::Equals:
              - Ref: AWS::Region
              - us-west-1
          - Fn::Equals:
              - Ref: AWS::Region
              - us-west-2
Parameters:
  BootstrapVersion:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /cdk-bootstrap/hnb659fds/version
    Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]
Rules:
  CheckBootstrapVersion:
    Assertions:
      - Assert:
          Fn::Not:
            - Fn::Contains:
                - - "1"
                  - "2"
                  - "3"
                  - "4"
                  - "5"
                - Ref: BootstrapVersion
        AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.

テストの実行

1.テスト用ライブラリのインストール

pytestを使えるようにするため、requirements-dev.txtを指定してpip installを行います。

// requirement-dev.txtの中身には、pytestが記載されています
cat requirements-dev.txt
pytest==6.2.5
(.venv) $ pip install -r requirements-dev.txt

Collecting pytest==6.2.5
  Using cached pytest-6.2.5-py3-none-any.whl (280 kB)
Collecting toml
  Using cached toml-0.10.2-py2.py3-none-any.whl (16 kB)
Collecting py>=1.8.2
  Using cached py-1.11.0-py2.py3-none-any.whl (98 kB)
Collecting iniconfig
  Using cached iniconfig-1.1.1-py2.py3-none-any.whl (5.0 kB)
Collecting pluggy<2.0,>=0.12
  Using cached pluggy-1.0.0-py2.py3-none-any.whl (13 kB)
Requirement already satisfied: attrs>=19.2.0 in ./.venv/lib/python3.8/site-packages (from pytest==6.2.5->-r requirements-dev.txt (line 1)) (22.1.0)
Collecting packaging
  Using cached packaging-21.3-py3-none-any.whl (40 kB)
Collecting pyparsing!=3.0.5,>=2.0.2
  Using cached pyparsing-3.0.9-py3-none-any.whl (98 kB)
Installing collected packages: iniconfig, toml, pyparsing, py, pluggy, packaging, pytest
Successfully installed iniconfig-1.1.1 packaging-21.3 pluggy-1.0.0 py-1.11.0 pyparsing-3.0.9 pytest-6.2.5 toml-0.10.2

2.テストコードの実行(pytest)

pytestコマンドを実行することでテストプログラムが実行されます。
まずは、cdk init sample-appで自動作成されたテストコードが正常に動作することを確認します。

// テストコードの実行
(.venv) $ pytest
================================================== test session starts ===================================================
platform darwin -- Python 3.8.13, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zigen/cdk_python
plugins: typeguard-2.13.3
collected 2 items                                                                                                        

tests/unit/test_cdk_python_stack.py ..                                                                             [100%]

=================================================== 2 passed in 6.96s ====================================================

上記のようなメッセージが表示されれば正常です。

それでは、テストコードで何を検証しているのか確認していきます。

3.スタックの内容

cdk_python_stack.py
・SQSキューの作成(可視性タイムアウトは300秒)
・SNSトピックの作成
・SNSトピックに送信されたメッセージをSQSに通知する
 (SNSに対してSQSに通知するサブスクリプションを追加)

from constructs import Construct
from aws_cdk import (
    Duration,
    Stack,
    aws_iam as iam,
    aws_sqs as sqs,
    aws_sns as sns,
    aws_sns_subscriptions as subs,
)

class CdkPythonStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        queue = sqs.Queue(
            self, "CdkPythonQueue",
            visibility_timeout=Duration.seconds(300),
        )

        topic = sns.Topic(
            self, "CdkPythonTopic"
        )

        topic.add_subscription(subs.SqsSubscription(queue))

4.テストコードの内容

test_cdk_python.stack.py

(1)test_sqs_queue_createdメソッド
 CloudFormationTemplateのリソースプロパティに
 "AWS::SQS::Queue", { "VisibilityTimeout": 300 }
 が含まれていることを検証する
 例)VisibilityTimeout(可視化タイムアウト)が100秒であればNG

(2)test_sns_topic_createdメソッド
 CloudFormationTemplateに
 "AWS::SNS::Topic"のリソースが 1つであることを検証する
 例)複数のトピックが定義されている場合はNG

import aws_cdk as core
import aws_cdk.assertions as assertions
from cdk_python.cdk_python_stack import CdkPythonStack


def test_sqs_queue_created():
    app = core.App()
    stack = CdkPythonStack(app, "cdk-python")
    template = assertions.Template.from_stack(stack)

    template.has_resource_properties("AWS::SQS::Queue", {
        "VisibilityTimeout": 300
    })


def test_sns_topic_created():
    app = core.App()
    stack = CdkPythonStack(app, "cdk-python")
    template = assertions.Template.from_stack(stack)

    template.resource_count_is("AWS::SNS::Topic", 1)

5.期待値を変更し、テストNGにしてみる

期待値を変更し、テスコードがNGになることを試してみます。

VisibilityTimeout(可視化タイムアウト)の期待値を300秒から100秒に変更します。

// VisibilityTimeoutの期待値を300から100に変更する

     template.has_resource_properties("AWS::SQS::Queue", {
        "VisibilityTimeout": 100
    })

pytestを実行すると、以下のようになります。

(.venv) $ pytest

:
E           jsii.errors.JSIIError: Template has 1 resources with type AWS::SQS::Queue, but none match as expected.
E           The closest result is:
E             {
E               "Type": "AWS::SQS::Queue",
E               "Properties": {
E                 "VisibilityTimeout": 300
E               },
E               "UpdateReplacePolicy": "Delete",
E               "DeletionPolicy": "Delete"
E             }
E           with the following mismatches:
E               Expected 100 but received 300 at /Properties/VisibilityTimeout (using objectLike matcher)

.venv/lib/python3.8/site-packages/jsii/_kernel/providers/process.py:329: JSIIError
================================================================ short test summary info ================================================================
FAILED tests/unit/test_cdk_python_stack.py::test_sqs_queue_created - jsii.errors.JSIIError: Template has 1 resources with type AWS::SQS::Queue, but no...
============================================================== 1 failed, 1 passed in 7.35s ==============================================================

"Expected 100 but received 300 at …"
(テストコードの期待値は100だが、300が設定されている)
というメッセージを確認できます。

続いて、SNSトピック数の期待値を1から2に変更します。

// SNSトピック数の期待値を1から2に変更する
template.resource_count_is("AWS::SNS::Topic", 2)

再度、pytestを実行します。

(.venv) $ pytest
:
: 省略
:

E           jsii.errors.JSIIError: Template has 1 resources with type AWS::SQS::Queue, but none match as expected.
E           The closest result is:
E             {
E               "Type": "AWS::SQS::Queue",
E               "Properties": {
E                 "VisibilityTimeout": 300
E               },
E               "UpdateReplacePolicy": "Delete",
E               "DeletionPolicy": "Delete"
E             }
E           with the following mismatches:
E               Expected 100 but received 300 at /Properties/VisibilityTimeout (using objectLike matcher)

.venv/lib/python3.8/site-packages/jsii/_kernel/providers/process.py:329: JSIIError
________________________________________________________________ test_sns_topic_created _________________________________________________________________
:
: 省略
:

E           jsii.errors.JSIIError: Expected 2 resources of type AWS::SNS::Topic but found 1

.venv/lib/python3.8/site-packages/jsii/_kernel/providers/process.py:329: JSIIError
================================================================ short test summary info ================================================================
FAILED tests/unit/test_cdk_python_stack.py::test_sqs_queue_created - jsii.errors.JSIIError: Template has 1 resources with type AWS::SQS::Queue, but no...
FAILED tests/unit/test_cdk_python_stack.py::test_sns_topic_created - jsii.errors.JSIIError: Expected 2 resources of type AWS::SNS::Topic but found 1
=================================================================== 2 failed in 6.76s ===================================================================

"Expected 2 resources of type AWS::SNS::Topic but found 1"
(SNSトピックのリソース数は期待値は2つだが、1つ見つかった)
というメッセージを確認することができます。

参考サイト(AWS公式)



この記事が気に入ったらサポートをしてみませんか?