

これは AWS CDK v2 デベロッパーガイドです。旧版の CDK v1 は 2022 年 6 月 1 日にメンテナンスを開始し、2023 年 6 月 1 日にサポートを終了しました。

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# AWS CDK アプリケーションのテスト
<a name="testing"></a>

 AWS CDK を使用すると、作成した他のコードと同様にインフラストラクチャをテストできます。クラウドおよびローカルでテストできます。このトピックでは、クラウドでテストする方法について説明します。ローカルテストのガイダンスについては、[「SAM CLI を使用した AWS CDK AWS アプリケーションのローカルテストと構築](testing-locally.md)」を参照してください。 AWS CDK アプリケーションをテストする標準的なアプローチでは、 AWS CDK の[アサーション](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.assertions-readme.html)モジュールと[、Jest](https://jestjs.io/) for TypeScript や JavaScript、[Pytest](https://docs.pytest.org/en/6.2.x/) for Python などの一般的なテストフレームワークを使用します。

 AWS CDK アプリ用に記述できるテストには 2 つのカテゴリがあります。
+  **きめ細かなアサーション**は、「このリソースにはこの値を持つこのプロパティがあります」など、生成された AWS CloudFormation テンプレートの特定の側面をテストします。これらのテストはリグレッションを検出できます。テスト駆動型開発を使用して新機能を開発するときにも役立ちます。(最初にテストを記述したら、正しい実装を記述して合格させることができます) きめ細かなアサーションは、最も頻繁に使用されるテストです。
+  **スナップショットテスト**では、以前に保存したベースラインテンプレートに対して合成された AWS CloudFormation テンプレートをテストします。リファクタリングされたコードが元のコードとまったく同じように動作することを確認できるため、スナップショットテストでは自由にリファクタリングできます。変更が意図的なものであった場合は、将来のテストのために新しいベースラインを受け入れることができます。ただし、CDK のアップグレードは合成されたテンプレートが変更される原因になる可能性もなるため、スナップショットのみに頼って実装が正しいことを確認できません。

**注記**  
このトピックで例として使用される TypeScript、Python、Java アプリの完全なバージョンは、[GitHub で入手できます](https://github.com/cdklabs/aws-cdk-testing-examples/)。

## 開始方法
<a name="testing-getting-started"></a>

これらのテストの記述方法を説明するために、 AWS Step Functions ステートマシンと AWS Lambda 関数を含むスタックを作成します。Lambda 関数は Amazon SNS トピックにサブスクライブされ、単にメッセージをステートマシンに転送します。

まず、CDK Toolkit を使用して空の CDK アプリケーションプロジェクトを作成し、必要なライブラリをインストールします。使用するコンストラクトはすべてメインの CDK パッケージに含まれ、CDK Toolkit で作成されたプロジェクトのデフォルトの依存関係です。ただし、テストフレームワークをインストールする必要があります。

**Example**  

```
$ mkdir state-machine && cd state-machine
cdk init --language=typescript
npm install --save-dev jest @types/jest
```
テスト用のディレクトリを作成します。  

```
$ mkdir test
```
プロジェクトの `package.json` を編集して NPM に Jest の実行方法を伝え、収集するファイルの種類を Jest に伝えます。必要な変更は次のとおりです。  
+ `scripts` セクションに新しい `test` キーの追加
+ `devDependencies` セクションに Jest とそのタイプの追加
+ `moduleFileExtensions` 宣言を含む新しい `jest` 最上位キーの追加
これらの変更は次の概要に示されます。`package.json` で示されている場所に新しいテキストを配置します。「...」プレースホルダーは、変更すべきではないファイルの既存の部分を示します。  

```
{
  ...
  "scripts": {
    ...
    "test": "jest"
  },
  "devDependencies": {
    ...
    "@types/jest": "^24.0.18",
    "jest": "^24.9.0"
  },
  "jest": {
    "moduleFileExtensions": ["js"]
  }
}
```

```
$ mkdir state-machine && cd state-machine
$ cdk init --language=javascript
$ npm install --save-dev jest
```
テスト用のディレクトリを作成します。  

```
$ mkdir test
```
プロジェクトの `package.json` を編集して NPM に Jest の実行方法を伝え、収集するファイルの種類を Jest に伝えます。必要な変更は次のとおりです。  
+ `scripts` セクションに新しい `test` キーの追加
+ Jest を `devDependencies` セクションに追加します。
+ `moduleFileExtensions` 宣言を含む新しい `jest` 最上位キーの追加
これらの変更は次の概要に示されます。`package.json` で示されている場所に新しいテキストを配置します。「...」プレースホルダーは、変更すべきではないファイルの既存の部分を示します。  

```
{
  ...
  "scripts": {
    ...
    "test": "jest"
  },
  "devDependencies": {
    ...
    "jest": "^24.9.0"
  },
  "jest": {
    "moduleFileExtensions": ["js"]
  }
}
```

```
$ mkdir state-machine && cd state-machine
$ cdk init --language=python
$ source .venv/bin/activate # On Windows, run '.\venv\Scripts\activate' instead
$ python -m pip install -r requirements.txt
$ python -m pip install -r requirements-dev.txt
```

```
$ mkdir state-machine && cd-state-machine
$ cdk init --language=java
```
任意の Java IDE でプロジェクトを開きます。(Eclipse で、**[ファイル]** > **[インポート]** > [既存の Maven プロジェクト] を使用します)

```
$ mkdir state-machine && cd-state-machine
$ cdk init --language=csharp
```
Visual Studio で `src\StateMachine.sln` を開きます。  
Solution Explorer でソリューションを右クリックし、**[追加]** > **[新規プロジェクト]** を選択します。MSTest C\$1 を検索し、C\$1 に **[MSTest テストプロジェクト]** を追加します。(デフォルト名「TestProject1」で問題ありません。)  
右クリック `TestProject1` して **[追加]** > **[プロジェクトリファレンス]** を選択し、`StateMachine` プロジェクトをリファレンスとして追加します。

## スタックの例
<a name="testing-app"></a>

このトピックでテストされるスタックは次のとおりです。前述のように、Lambda 関数および Step Functions ステートマシンが含まれており、1 つ以上の Amazon SNS トピックを受け入れます。Lambda 関数は Amazon SNS トピックにサブスクライブされ、ステートマシンに転送されます。

アプリをテスト可能にするために特別な操作をする必要はありません。実際、この CDK スタックは、このガイドの他のスタック例とは重要な点で異なりません。

**Example**  
`lib/state-machine-stack.ts` に次のコードを貼り付けます。  

```
import * as cdk from "aws-cdk-lib";
import * as sns from "aws-cdk-lib/aws-sns";
import * as sns_subscriptions from "aws-cdk-lib/aws-sns-subscriptions";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as sfn from "aws-cdk-lib/aws-stepfunctions";
import { Construct } from "constructs";

export interface StateMachineStackProps extends cdk.StackProps {
  readonly topics: sns.Topic[];
}

export class StateMachineStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: StateMachineStackProps) {
    super(scope, id, props);

    // In the future this state machine will do some work...
    const stateMachine = new sfn.StateMachine(this, "StateMachine", {
      definition: new sfn.Pass(this, "StartState"),
    });

    // This Lambda function starts the state machine.
    const func = new lambda.Function(this, "LambdaFunction", {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: "handler",
      code: lambda.Code.fromAsset("./start-state-machine"),
      environment: {
        STATE_MACHINE_ARN: stateMachine.stateMachineArn,
      },
    });
    stateMachine.grants.startExecution(func);

    const subscription = new sns_subscriptions.LambdaSubscription(func);
    for (const topic of props.topics) {
      topic.addSubscription(subscription);
    }
  }
}
```
`lib/state-machine-stack.js` に次のコードを貼り付けます。  

```
const cdk = require("aws-cdk-lib");
const sns = require("aws-cdk-lib/aws-sns");
const sns_subscriptions = require("aws-cdk-lib/aws-sns-subscriptions");
const lambda = require("aws-cdk-lib/aws-lambda");
const sfn = require("aws-cdk-lib/aws-stepfunctions");

class StateMachineStack extends cdk.Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    // In the future this state machine will do some work...
    const stateMachine = new sfn.StateMachine(this, "StateMachine", {
      definition: new sfn.Pass(this, "StartState"),
    });

    // This Lambda function starts the state machine.
    const func = new lambda.Function(this, "LambdaFunction", {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: "handler",
      code: lambda.Code.fromAsset("./start-state-machine"),
      environment: {
        STATE_MACHINE_ARN: stateMachine.stateMachineArn,
      },
    });
    stateMachine.grants.startExecution(func);

    const subscription = new sns_subscriptions.LambdaSubscription(func);
    for (const topic of props.topics) {
      topic.addSubscription(subscription);
    }
  }
}

module.exports = { StateMachineStack }
```
`state_machine/state_machine_stack.py` に次のコードを貼り付けます。  

```
from typing import List

import aws_cdk.aws_lambda as lambda_
import aws_cdk.aws_sns as sns
import aws_cdk.aws_sns_subscriptions as sns_subscriptions
import aws_cdk.aws_stepfunctions as sfn
import aws_cdk as cdk

class StateMachineStack(cdk.Stack):
    def __init__(
        self,
        scope: cdk.Construct,
        construct_id: str,
        *,
        topics: List[sns.Topic],
        **kwargs
    ) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # In the future this state machine will do some work...
        state_machine = sfn.StateMachine(
            self, "StateMachine", definition=sfn.Pass(self, "StartState")
        )

        # This Lambda function starts the state machine.
        func = lambda_.Function(
            self,
            "LambdaFunction",
            runtime=lambda_.Runtime.NODEJS_18_X,
            handler="handler",
            code=lambda_.Code.from_asset("./start-state-machine"),
            environment={
                "STATE_MACHINE_ARN": state_machine.state_machine_arn,
            },
        )
        state_machine.grants.start_execution(func)

        subscription = sns_subscriptions.LambdaSubscription(func)
        for topic in topics:
            topic.add_subscription(subscription)
```

```
package software.amazon.samples.awscdkassertionssamples;

import software.constructs.Construct;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.lambda.Code;
import software.amazon.awscdk.services.lambda.Function;
import software.amazon.awscdk.services.lambda.Runtime;
import software.amazon.awscdk.services.sns.ITopicSubscription;
import software.amazon.awscdk.services.sns.Topic;
import software.amazon.awscdk.services.sns.subscriptions.LambdaSubscription;
import software.amazon.awscdk.services.stepfunctions.Pass;
import software.amazon.awscdk.services.stepfunctions.StateMachine;

import java.util.Collections;
import java.util.List;

public class StateMachineStack extends Stack {
    public StateMachineStack(final Construct scope, final String id, final List<Topic> topics) {
        this(scope, id, null, topics);
    }

    public StateMachineStack(final Construct scope, final String id, final StackProps props, final List<Topic> topics) {
        super(scope, id, props);

        // In the future this state machine will do some work...
        final StateMachine stateMachine = StateMachine.Builder.create(this, "StateMachine")
                .definition(new Pass(this, "StartState"))
                .build();

        // This Lambda function starts the state machine.
        final Function func = Function.Builder.create(this, "LambdaFunction")
                .runtime(Runtime.NODEJS_18_X)
                .handler("handler")
                .code(Code.fromAsset("./start-state-machine"))
                .environment(Collections.singletonMap("STATE_MACHINE_ARN", stateMachine.getStateMachineArn()))
                .build();
        stateMachine.getGrants().startExecution(func);

        final ITopicSubscription subscription = new LambdaSubscription(func);
        for (final Topic topic : topics) {
            topic.addSubscription(subscription);
        }
    }
}
```

```
using Amazon.CDK;
using Amazon.CDK.AWS.Lambda;
using Amazon.CDK.AWS.StepFunctions;
using Amazon.CDK.AWS.SNS;
using Amazon.CDK.AWS.SNS.Subscriptions;
using Constructs;

using System.Collections.Generic;

namespace AwsCdkAssertionSamples
{
    public class StateMachineStackProps : StackProps
    {
        public Topic[] Topics;
    }

    public class StateMachineStack : Stack
    {

        internal StateMachineStack(Construct scope, string id, StateMachineStackProps props = null) : base(scope, id, props)
        {
            // In the future this state machine will do some work...
            var stateMachine = new StateMachine(this, "StateMachine", new StateMachineProps
            {
                Definition = new Pass(this, "StartState")
            });

            // This Lambda function starts the state machine.
            var func = new Function(this, "LambdaFunction", new FunctionProps
            {
                Runtime = Runtime.NODEJS_18_X,
                Handler = "handler",
                Code = Code.FromAsset("./start-state-machine"),
                Environment = new Dictionary<string, string>
                {
                    { "STATE_MACHINE_ARN", stateMachine.StateMachineArn }
                }
            });
            stateMachine.Grants.StartExecution(func);

            foreach (Topic topic in props?.Topics ?? new Topic[0])
            {
                var subscription = new LambdaSubscription(func);
            }

        }
    }
}
```

スタックを実際にインスタンス化しないように、アプリのメインエントリポイントを変更します。誤ってデプロイすることを回避する必要があります。テストは、テスト用のアプリおよびスタックのインスタンスが作成されます。テスト駆動型開発と組み合わせると便利な戦術です。デプロイを有効にする前に、スタックがすべてのテストに合格していることを確認してください。

**Example**  
Eclipse `bin/state-machine.ts`:  

```
#!/usr/bin/env node
import * as cdk from "aws-cdk-lib";

const app = new cdk.App();

// Stacks are intentionally not created here -- this application isn't meant to
// be deployed.
```
Eclipse `bin/state-machine.js`:  

```
#!/usr/bin/env node
const cdk = require("aws-cdk-lib");

const app = new cdk.App();

// Stacks are intentionally not created here -- this application isn't meant to
// be deployed.
```
Eclipse `app.py`:  

```
#!/usr/bin/env python3
import os

import aws_cdk  as cdk

app = cdk.App()

# Stacks are intentionally not created here -- this application isn't meant to
# be deployed.

app.synth()
```

```
package software.amazon.samples.awscdkassertionssamples;

import software.amazon.awscdk.App;

public class SampleApp {
    public static void main(final String[] args) {
        App app = new App();

        // Stacks are intentionally not created here -- this application isn't meant to be deployed.

        app.synth();
    }
}
```

```
using Amazon.CDK;

namespace AwsCdkAssertionSamples
{
    sealed class Program
    {
        public static void Main(string[] args)
        {
            var app = new App();

            // Stacks are intentionally not created here -- this application isn't meant to be deployed.

            app.Synth();
        }
    }
}
```

## Lambda 関数
<a name="testing-lambda"></a>

スタック例には、ステートマシンを起動する Lambda 関数が含まれています。CDK が Lambda 関数リソースの作成の一環としてバンドルしてデプロイできるように、この関数のソースコードを指定する必要があります。
+ アプリのメインディレクトリにフォルダ `start-state-machine` を作成します。
+ このフォルダには、少なくとも 1 つのファイルを作成します。例えば、次のコードを `start-state-machines/index.js` に保存できます。

  ```
  exports.handler = async function (event, context) {
  	return 'hello world';
  };
  ```

  ただし、実際にスタックをデプロイしないため、どんなファイルでも機能します。

## テストを実行する
<a name="testing-running-tests"></a>

参考までに、 AWS CDK アプリでテストを実行するために使用するコマンドを次に示します。同じテストフレームワークを使用して任意のプロジェクトでテストを実行するために使用するコマンドと同じです。ビルドステップが必要な言語の場合、テストでコンパイルされることを確認するために含めてください。

**Example**  

```
$ tsc && npm test
```

```
$ npm test
```

```
$ python -m pytest
```

```
$ mvn compile && mvn test
```
ソリューション (F6) を構築してテストを経験したら、テストを実行します (**[テスト]** > **[すべてのテストの実行]**)。実行するテストを選択するには、Test Explorer を開きます (**[テスト]** > **[Test Explorer]**)。  
または:  

```
$ dotnet test src
```

## きめ細かなアサーション
<a name="testing-fine-grained"></a>

きめ細かなアサーションを使用してスタックをテストする最初のステップは、生成された AWS CloudFormation テンプレートに対してアサーションを記述するため、スタックを合成することです。

`StateMachineStackStack` では、Amazon SNS トピックがステートマシンに転送されるために渡す必要があります。そのため、テストにはトピックを含む別のスタックを作成します。

通常、CDK アプリを記述するとき、スタックのコンストラクタで Amazon SNS トピックをサブクラス `Stack` およびインスタンス化できます。テストでは、`Stack` を直接インスタンス化したら、このスタックを `Topic` のスコープとして渡してスタックにアタッチします。これは機能的に同等であり、それほど冗長ではありません。テストでのみ使用されるスタックを、デプロイするスタックとは「見かけが異なるもの」にするのうえで役立ちます。

**Example**  

```
import { Capture, Match, Template } from "aws-cdk-lib/assertions";
import * as cdk from "aws-cdk-lib";
import * as sns from "aws-cdk-lib/aws-sns";
import { StateMachineStack } from "../lib/state-machine-stack";

describe("StateMachineStack", () => {
  test("synthesizes the way we expect", () => {
    const app = new cdk.App();

    // Since the StateMachineStack consumes resources from a separate stack
    // (cross-stack references), we create a stack for our SNS topics to live
    // in here. These topics can then be passed to the StateMachineStack later,
    // creating a cross-stack reference.
    const topicsStack = new cdk.Stack(app, "TopicsStack");

    // Create the topic the stack we're testing will reference.
    const topics = [new sns.Topic(topicsStack, "Topic1", {})];

    // Create the StateMachineStack.
    const stateMachineStack = new StateMachineStack(app, "StateMachineStack", {
      topics: topics, // Cross-stack reference
    });

    // Prepare the stack for assertions.
    const template = Template.fromStack(stateMachineStack);
  })
})
```

```
const { Capture, Match, Template } = require("aws-cdk-lib/assertions");
const cdk = require("aws-cdk-lib");
const sns = require("aws-cdk-lib/aws-sns");
const { StateMachineStack } = require("../lib/state-machine-stack");

describe("StateMachineStack", () => {
  test("synthesizes the way we expect", () => {
    const app = new cdk.App();

    // Since the StateMachineStack consumes resources from a separate stack
    // (cross-stack references), we create a stack for our SNS topics to live
    // in here. These topics can then be passed to the StateMachineStack later,
    // creating a cross-stack reference.
    const topicsStack = new cdk.Stack(app, "TopicsStack");

    // Create the topic the stack we're testing will reference.
    const topics = [new sns.Topic(topicsStack, "Topic1", {})];

    // Create the StateMachineStack.
    const StateMachineStack = new StateMachineStack(app, "StateMachineStack", {
      topics: topics, // Cross-stack reference
    });

    // Prepare the stack for assertions.
    const template = Template.fromStack(stateMachineStack);
  })
})
```

```
from aws_cdk import aws_sns as sns
import aws_cdk as cdk
from aws_cdk.assertions import Template

from app.state_machine_stack import StateMachineStack

def test_synthesizes_properly():
    app = cdk.App()

    # Since the StateMachineStack consumes resources from a separate stack
    # (cross-stack references), we create a stack for our SNS topics to live
    # in here. These topics can then be passed to the StateMachineStack later,
    # creating a cross-stack reference.
    topics_stack = cdk.Stack(app, "TopicsStack")

    # Create the topic the stack we're testing will reference.
    topics = [sns.Topic(topics_stack, "Topic1")]

    # Create the StateMachineStack.
    state_machine_stack = StateMachineStack(
        app, "StateMachineStack", topics=topics  # Cross-stack reference
    )

    # Prepare the stack for assertions.
    template = Template.from_stack(state_machine_stack)
```

```
package software.amazon.samples.awscdkassertionssamples;

import org.junit.jupiter.api.Test;
import software.amazon.awscdk.assertions.Capture;
import software.amazon.awscdk.assertions.Match;
import software.amazon.awscdk.assertions.Template;
import software.amazon.awscdk.App;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.services.sns.Topic;

import java.util.*;

import static org.assertj.core.api.Assertions.assertThat;

public class StateMachineStackTest {
    @Test
    public void testSynthesizesProperly() {
        final App app = new App();

        // Since the StateMachineStack consumes resources from a separate stack (cross-stack references), we create a stack
        // for our SNS topics to live in here. These topics can then be passed to the StateMachineStack later, creating a
        // cross-stack reference.
        final Stack topicsStack = new Stack(app, "TopicsStack");

        // Create the topic the stack we're testing will reference.
        final List<Topic> topics = Collections.singletonList(Topic.Builder.create(topicsStack, "Topic1").build());

        // Create the StateMachineStack.
        final StateMachineStack stateMachineStack = new StateMachineStack(
                app,
                "StateMachineStack",
                topics // Cross-stack reference
        );

        // Prepare the stack for assertions.
        final Template template = Template.fromStack(stateMachineStack)
    }
}
```

```
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Amazon.CDK;
using Amazon.CDK.AWS.SNS;
using Amazon.CDK.Assertions;
using AwsCdkAssertionSamples;

using ObjectDict = System.Collections.Generic.Dictionary<string, object>;
using StringDict = System.Collections.Generic.Dictionary<string, string>;

namespace TestProject1
{
    [TestClass]
    public class StateMachineStackTest
    {
        [TestMethod]
        public void TestMethod1()
        {
            var app = new App();

            // Since the StateMachineStack consumes resources from a separate stack (cross-stack references), we create a stack
            // for our SNS topics to live in here. These topics can then be passed to the StateMachineStack later, creating a
            // cross-stack reference.
            var topicsStack = new Stack(app, "TopicsStack");

            // Create the topic the stack we're testing will reference.
            var topics = new Topic[] { new Topic(topicsStack, "Topic1") };

            // Create the StateMachineStack.
            var StateMachineStack = new StateMachineStack(app, "StateMachineStack", new StateMachineStackProps
            {
                Topics = topics
            });

            // Prepare the stack for assertions.
            var template = Template.FromStack(stateMachineStack);

            // test will go here
        }
    }
}
```

Lambda 関数および Amazon SNS サブスクリプションが作成されたことをアサートできます。

**Example**  

```
    // Assert it creates the function with the correct properties...
    template.hasResourceProperties("AWS::Lambda::Function", {
      Handler: "handler",
      Runtime: "nodejs14.x",
    });

    // Creates the subscription...
    template.resourceCountIs("AWS::SNS::Subscription", 1);
```

```
    // Assert it creates the function with the correct properties...
    template.hasResourceProperties("AWS::Lambda::Function", {
      Handler: "handler",
      Runtime: "nodejs14.x",
    });

    // Creates the subscription...
    template.resourceCountIs("AWS::SNS::Subscription", 1);
```

```
# Assert that we have created the function with the correct properties
    template.has_resource_properties(
        "AWS::Lambda::Function",
        {
            "Handler": "handler",
            "Runtime": "nodejs14.x",
        },
    )

    # Assert that we have created a subscription
    template.resource_count_is("AWS::SNS::Subscription", 1)
```

```
        // Assert it creates the function with the correct properties...
        template.hasResourceProperties("AWS::Lambda::Function", Map.of(
                "Handler", "handler",
                "Runtime", "nodejs14.x"
        ));

         // Creates the subscription...
        template.resourceCountIs("AWS::SNS::Subscription", 1);
```

```
            // Prepare the stack for assertions.
            var template = Template.FromStack(stateMachineStack);

            // Assert it creates the function with the correct properties...
            template.HasResourceProperties("AWS::Lambda::Function", new StringDict {
                { "Handler", "handler"},
                { "Runtime", "nodejs14x" }
            });

            // Creates the subscription...
            template.ResourceCountIs("AWS::SNS::Subscription", 1);
```

Lambda 関数テストは、関数リソースの 2 つの特定プロパティに特定の値があることをアサートします。デフォルトでは、`hasResourceProperties` メソッドは合成された CloudFormation テンプレートで、指定されたリソースのプロパティに対して部分一致を実行します。このテストでは、指定されたプロパティが存在して指定された値を持つ必要がありますが、リソースにはテストされていない他のプロパティを持つこともできます。

Amazon SNS アサーションは、合成されたテンプレートにサブスクリプションが含まれているが、サブスクリプション自体については何も含まれていないことをアサートします。このアサーションは、主にリソース数についてアサートする方法を説明するために含めました。`Template` クラスには、CloudFormation テンプレートの `Resources`、`Outputs`、`Mapping` セクションに対してアサーションを記述するより具体的な方法が用意されています。<a name="testing-fine-grained-matchers"></a>

 **マッチャー**   
`hasResourceProperties` のデフォルトの部分一致動作は、` [Match](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.assertions.Match.html#methods) ` クラスの*マッチャー*を使用して変更できます。  
マッチャーの範囲は寛容 (`Match.anyValue`) から厳格 (`Match.objectEquals`) まであります。リソースプロパティのさまざまな部分にさまざまなマッチングメソッドを適用するためにネストできます。例えば、`Match.objectEquals` および `Match.anyValue` を併用すると、ステートマシンの IAM ロールをより完全にテストできますが、変更される可能性のあるプロパティに特定の値は必要ありません。  

**Example**  

```
    // Fully assert on the state machine's IAM role with matchers.
    template.hasResourceProperties(
      "AWS::IAM::Role",
      Match.objectEquals({
        AssumeRolePolicyDocument: {
          Version: "2012-10-17",		 	 	 
          Statement: [
            {
              Action: "sts:AssumeRole",
              Effect: "Allow",
              Principal: {
                Service: {
                  "Fn::Join": [
                    "",
                    ["states.", Match.anyValue(), ".amazonaws.com"],
                  ],
                },
              },
            },
          ],
        },
      })
    );
```

```
    // Fully assert on the state machine's IAM role with matchers.
    template.hasResourceProperties(
      "AWS::IAM::Role",
      Match.objectEquals({
        AssumeRolePolicyDocument: {
          Version: "2012-10-17",		 	 	 
          Statement: [
            {
              Action: "sts:AssumeRole",
              Effect: "Allow",
              Principal: {
                Service: {
                  "Fn::Join": [
                    "",
                    ["states.", Match.anyValue(), ".amazonaws.com"],
                  ],
                },
              },
            },
          ],
        },
      })
    );
```

```
from aws_cdk.assertions import Match

    # Fully assert on the state machine's IAM role with matchers.
    template.has_resource_properties(
        "AWS::IAM::Role",
        Match.object_equals(
            {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",		 	 	 
                    "Statement": [
                        {
                            "Action": "sts:AssumeRole",
                            "Effect": "Allow",
                            "Principal": {
                                "Service": {
                                    "Fn::Join": [
                                        "",
                                        [
                                            "states.",
                                            Match.any_value(),
                                            ".amazonaws.com",
                                        ],
                                    ],
                                },
                            },
                        },
                    ],
                },
            }
        ),
    )
```

```
        // Fully assert on the state machine's IAM role with matchers.
        template.hasResourceProperties("AWS::IAM::Role", Match.objectEquals(
                Collections.singletonMap("AssumeRolePolicyDocument", Map.of(
                        "Version", "2012-10-17",		 	 	 
                        "Statement", Collections.singletonList(Map.of(
                                "Action", "sts:AssumeRole",
                                "Effect", "Allow",
                                "Principal", Collections.singletonMap(
                                        "Service", Collections.singletonMap(
                                                "Fn::Join", Arrays.asList(
                                                        "",
                                                        Arrays.asList("states.", Match.anyValue(), ".amazonaws.com")
                                                )
                                        )
                                )
                        ))
                ))
        ));
```

```
            // Fully assert on the state machine's IAM role with matchers.
            template.HasResource("AWS::IAM::Role", Match.ObjectEquals(new ObjectDict
            {
                { "AssumeRolePolicyDocument", new ObjectDict
                    {
                        { "Version", "2012-10-17"		 	 	  },
                        { "Action", "sts:AssumeRole" },
                        { "Principal", new ObjectDict
                            {
                                { "Version", "2012-10-17"		 	 	  },
                                { "Statement", new object[]
                                    {
                                        new ObjectDict {
                                            { "Action", "sts:AssumeRole" },
                                            { "Effect", "Allow" },
                                            { "Principal", new ObjectDict
                                                {
                                                    { "Service", new ObjectDict
                                                        {
                                                            { "", new object[]
                                                                { "states", Match.AnyValue(), ".amazonaws.com" }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }));
```
多くの CloudFormation リソースには、文字列として表されるシリアル化された JSON オブジェクトが含まれています。`Match.serializedJson()` マッチャーは、この JSON 内のプロパティを一致させるために使用できます。  
例えば、Step Functions ステートマシンは JSON ベースの [Amazon States Language ](https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html) の文字列を使用して定義されます。`Match.serializedJson()` を使用し、最初の状態が唯一のステップであることを確認します。ここでもネストされたマッチャーを使用して、オブジェクトのさまざまな部分にさまざまなマッチングの種類を適用します。  

**Example**  

```
    // Assert on the state machine's definition with the Match.serializedJson()
    // matcher.
    template.hasResourceProperties("AWS::StepFunctions::StateMachine", {
      DefinitionString: Match.serializedJson(
        // Match.objectEquals() is used implicitly, but we use it explicitly
        // here for extra clarity.
        Match.objectEquals({
          StartAt: "StartState",
          States: {
            StartState: {
              Type: "Pass",
              End: true,
              // Make sure this state doesn't provide a next state -- we can't
              // provide both Next and set End to true.
              Next: Match.absent(),
            },
          },
        })
      ),
    });
```

```
    // Assert on the state machine's definition with the Match.serializedJson()
    // matcher.
    template.hasResourceProperties("AWS::StepFunctions::StateMachine", {
      DefinitionString: Match.serializedJson(
        // Match.objectEquals() is used implicitly, but we use it explicitly
        // here for extra clarity.
        Match.objectEquals({
          StartAt: "StartState",
          States: {
            StartState: {
              Type: "Pass",
              End: true,
              // Make sure this state doesn't provide a next state -- we can't
              // provide both Next and set End to true.
              Next: Match.absent(),
            },
          },
        })
      ),
    });
```

```
    # Assert on the state machine's definition with the serialized_json matcher.
    template.has_resource_properties(
        "AWS::StepFunctions::StateMachine",
        {
            "DefinitionString": Match.serialized_json(
                # Match.object_equals() is the default, but specify it here for clarity
                Match.object_equals(
                    {
                        "StartAt": "StartState",
                        "States": {
                            "StartState": {
                                "Type": "Pass",
                                "End": True,
                                # Make sure this state doesn't provide a next state --
                                # we can't provide both Next and set End to true.
                                "Next": Match.absent(),
                            },
                        },
                    }
                )
            ),
        },
    )
```

```
        // Assert on the state machine's definition with the Match.serializedJson() matcher.
        template.hasResourceProperties("AWS::StepFunctions::StateMachine", Collections.singletonMap(
                "DefinitionString", Match.serializedJson(
                        // Match.objectEquals() is used implicitly, but we use it explicitly here for extra clarity.
                        Match.objectEquals(Map.of(
                                "StartAt", "StartState",
                                "States", Collections.singletonMap(
                                        "StartState", Map.of(
                                                "Type", "Pass",
                                                "End", true,
                                                // Make sure this state doesn't provide a next state -- we can't provide
                                                // both Next and set End to true.
                                                "Next", Match.absent()
                                        )
                                )
                        ))
                )
        ));
```

```
            // Assert on the state machine's definition with the Match.serializedJson() matcher
            template.HasResourceProperties("AWS::StepFunctions::StateMachine", new ObjectDict
            {
                { "DefinitionString", Match.SerializedJson(
                    // Match.objectEquals() is used implicitly, but we use it explicitly here for extra clarity.
                    Match.ObjectEquals(new ObjectDict {
                        { "StartAt", "StartState" },
                        { "States", new ObjectDict
                        {
                            { "StartState", new ObjectDict {
                                { "Type", "Pass" },
                                { "End", "True" },
                                // Make sure this state doesn't provide a next state -- we can't provide
                                // both Next and set End to true.
                                { "Next", Match.Absent() }
                            }}
                        }}
                    })
                )}});
```<a name="testing-fine-grained-capture"></a>

 **キャプチャ**   
事前に正確な値を知る必要がなく、プロパティをテストして特定の形式に従うか、別のプロパティと同じ値を持つことを確認すると便利な場合がよくあります。`assertions` モジュールは、` [Capture](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.assertions.Capture.html) ` クラスでこの機能を提供します。  
`hasResourceProperties` の値の代わりに `Capture` インスタンスを指定することにより、その値は `Capture` オブジェクトに保持されます。実際のキャプチャ値は、テストの対象となる `asNumber()`、`asString()`、`asObject` など、オブジェクトの `as` メソッドを使用して取得できます。マッチャーと一緒に `Capture` を使用し、シリアル化された JSON プロパティなど、リソースのプロパティ内でキャプチャされる値の正確な場所を指定します。  
次の例では、ステートマシンの開始状態が `Start` で始まる名前を持っていることを確認するテストを実行します。この状態がマシンの状態のリスト内に存在することをテストします。  

**Example**  

```
    // Capture some data from the state machine's definition.
    const startAtCapture = new Capture();
    const statesCapture = new Capture();
    template.hasResourceProperties("AWS::StepFunctions::StateMachine", {
      DefinitionString: Match.serializedJson(
        Match.objectLike({
          StartAt: startAtCapture,
          States: statesCapture,
        })
      ),
    });

    // Assert that the start state starts with "Start".
    expect(startAtCapture.asString()).toEqual(expect.stringMatching(/^Start/));

    // Assert that the start state actually exists in the states object of the
    // state machine definition.
    expect(statesCapture.asObject()).toHaveProperty(startAtCapture.asString());
```

```
    // Capture some data from the state machine's definition.
    const startAtCapture = new Capture();
    const statesCapture = new Capture();
    template.hasResourceProperties("AWS::StepFunctions::StateMachine", {
      DefinitionString: Match.serializedJson(
        Match.objectLike({
          StartAt: startAtCapture,
          States: statesCapture,
        })
      ),
    });

    // Assert that the start state starts with "Start".
    expect(startAtCapture.asString()).toEqual(expect.stringMatching(/^Start/));

    // Assert that the start state actually exists in the states object of the
    // state machine definition.
    expect(statesCapture.asObject()).toHaveProperty(startAtCapture.asString());
```

```
import re

    from aws_cdk.assertions import Capture

    # ...

    # Capture some data from the state machine's definition.
    start_at_capture = Capture()
    states_capture = Capture()
    template.has_resource_properties(
        "AWS::StepFunctions::StateMachine",
        {
            "DefinitionString": Match.serialized_json(
                Match.object_like(
                    {
                        "StartAt": start_at_capture,
                        "States": states_capture,
                    }
                )
            ),
        },
    )

    # Assert that the start state starts with "Start".
    assert re.match("^Start", start_at_capture.as_string())

    # Assert that the start state actually exists in the states object of the
    # state machine definition.
    assert start_at_capture.as_string() in states_capture.as_object()
```

```
        // Capture some data from the state machine's definition.
        final Capture startAtCapture = new Capture();
        final Capture statesCapture = new Capture();
        template.hasResourceProperties("AWS::StepFunctions::StateMachine", Collections.singletonMap(
                "DefinitionString", Match.serializedJson(
                        Match.objectLike(Map.of(
                                "StartAt", startAtCapture,
                                "States", statesCapture
                        ))
                )
        ));

        // Assert that the start state starts with "Start".
        assertThat(startAtCapture.asString()).matches("^Start.+");

        // Assert that the start state actually exists in the states object of the state machine definition.
        assertThat(statesCapture.asObject()).containsKey(startAtCapture.asString());
```

```
            // Capture some data from the state machine's definition.
            var startAtCapture = new Capture();
            var statesCapture = new Capture();
            template.HasResourceProperties("AWS::StepFunctions::StateMachine", new ObjectDict
            {
                { "DefinitionString", Match.SerializedJson(
                    new ObjectDict
                    {
                        { "StartAt", startAtCapture },
                        { "States", statesCapture }
                    }
                )}
            });

            Assert.IsTrue(startAtCapture.ToString().StartsWith("Start"));
            Assert.IsTrue(statesCapture.AsObject().ContainsKey(startAtCapture.ToString()));
```

## スナップショットテスト
<a name="testing-snapshot"></a>

*スナップショットテスト* では、合成された CloudFormation テンプレート全体を、以前に保存されたベースライン (多くの場合、「マスター」と呼ばれます) テンプレートと比較します。きめ細かなアサーションとは異なり、スナップショットテストはリグレッションのキャッチには役に立ちません。スナップショットテストがテンプレート全体に適用され、コード変更以外のことが合成結果に小さな (またはそれほど小さくない) 差を引き起こす可能性があるためです。これらの変更はデプロイにも影響しない可能性がありますが、スナップショットテストは失敗します。

例えば、CDK コンストラクトを更新して新しいベストプラクティスを組み込むと、合成されたリソースやその編成方法が変更される可能性があります。または、追加のメタデータをレポートするバージョンに CDK Toolkit を更新できます。コンテキスト値を変更すると、合成されたテンプレートにも影響します。

ただし、合成されたテンプレートに影響を与える可能性のある他のすべての要因を一定に保つ限り、スナップショットテストはリファクタリングに非常に役立ちます。行った変更がテンプレートを誤って変更したかどうかについて、すぐにわかります。変更が意図的な場合、新しいテンプレートをベースラインとして受け入れるのみです。

例えば、この `DeadLetterQueue` コンストラクトがある場合

**Example**  

```
export class DeadLetterQueue extends sqs.Queue {
  public readonly messagesInQueueAlarm: cloudwatch.IAlarm;

  constructor(scope: Construct, id: string) {
    super(scope, id);

    // Add the alarm
    this.messagesInQueueAlarm = new cloudwatch.Alarm(this, 'Alarm', {
      alarmDescription: 'There are messages in the Dead Letter Queue',
      evaluationPeriods: 1,
      threshold: 1,
      metric: this.metricApproximateNumberOfMessagesVisible(),
    });
  }
}
```

```
class DeadLetterQueue extends sqs.Queue {

  constructor(scope, id) {
    super(scope, id);

    // Add the alarm
    this.messagesInQueueAlarm = new cloudwatch.Alarm(this, 'Alarm', {
      alarmDescription: 'There are messages in the Dead Letter Queue',
      evaluationPeriods: 1,
      threshold: 1,
      metric: this.metricApproximateNumberOfMessagesVisible(),
    });
  }
}

module.exports = { DeadLetterQueue }
```

```
class DeadLetterQueue(sqs.Queue):
    def __init__(self, scope: Construct, id: str):
        super().__init__(scope, id)

        self.messages_in_queue_alarm = cloudwatch.Alarm(
            self,
            "Alarm",
            alarm_description="There are messages in the Dead Letter Queue.",
            evaluation_periods=1,
            threshold=1,
            metric=self.metric_approximate_number_of_messages_visible(),
        )
```

```
public class DeadLetterQueue extends Queue {
    private final IAlarm messagesInQueueAlarm;

    public DeadLetterQueue(@NotNull Construct scope, @NotNull String id) {
        super(scope, id);

        this.messagesInQueueAlarm = Alarm.Builder.create(this, "Alarm")
                .alarmDescription("There are messages in the Dead Letter Queue.")
                .evaluationPeriods(1)
                .threshold(1)
                .metric(this.metricApproximateNumberOfMessagesVisible())
                .build();
    }

    public IAlarm getMessagesInQueueAlarm() {
        return messagesInQueueAlarm;
    }
}
```

```
namespace AwsCdkAssertionSamples
{
    public class DeadLetterQueue : Queue
    {
        public IAlarm messagesInQueueAlarm;

        public DeadLetterQueue(Construct scope, string id) : base(scope, id)
        {
            messagesInQueueAlarm = new Alarm(this, "Alarm", new AlarmProps
            {
                AlarmDescription = "There are messages in the Dead Letter Queue.",
                EvaluationPeriods = 1,
                Threshold = 1,
                Metric = this.MetricApproximateNumberOfMessagesVisible()
            });
        }
    }
}
```

次のようにテストできます。

**Example**  

```
import { Match, Template } from "aws-cdk-lib/assertions";
import * as cdk from "aws-cdk-lib";
import { DeadLetterQueue } from "../lib/dead-letter-queue";

describe("DeadLetterQueue", () => {
  test("matches the snapshot", () => {
    const stack = new cdk.Stack();
    new DeadLetterQueue(stack, "DeadLetterQueue");

    const template = Template.fromStack(stack);
    expect(template.toJSON()).toMatchSnapshot();
  });
});
```

```
const { Match, Template } = require("aws-cdk-lib/assertions");
const cdk = require("aws-cdk-lib");
const { DeadLetterQueue } = require("../lib/dead-letter-queue");

describe("DeadLetterQueue", () => {
  test("matches the snapshot", () => {
    const stack = new cdk.Stack();
    new DeadLetterQueue(stack, "DeadLetterQueue");

    const template = Template.fromStack(stack);
    expect(template.toJSON()).toMatchSnapshot();
  });
});
```

```
import aws_cdk_lib as cdk
from aws_cdk_lib.assertions import Match, Template

from app.dead_letter_queue import DeadLetterQueue

def snapshot_test():
    stack = cdk.Stack()
    DeadLetterQueue(stack, "DeadLetterQueue")

    template = Template.from_stack(stack)
    assert template.to_json() == snapshot
```

```
package software.amazon.samples.awscdkassertionssamples;

import org.junit.jupiter.api.Test;
import au.com.origin.snapshots.Expect;
import software.amazon.awscdk.assertions.Match;
import software.amazon.awscdk.assertions.Template;
import software.amazon.awscdk.Stack;

import java.util.Collections;
import java.util.Map;

public class DeadLetterQueueTest {
    @Test
    public void snapshotTest() {
        final Stack stack = new Stack();
        new DeadLetterQueue(stack, "DeadLetterQueue");

        final Template template = Template.fromStack(stack);
        expect.toMatchSnapshot(template.toJSON());
    }
}
```

```
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Amazon.CDK;
using Amazon.CDK.Assertions;
using AwsCdkAssertionSamples;

using ObjectDict = System.Collections.Generic.Dictionary<string, object>;
using StringDict = System.Collections.Generic.Dictionary<string, string>;

namespace TestProject1
{
    [TestClass]
    public class StateMachineStackTest

    [TestClass]
    public class DeadLetterQueueTest
    {
    [TestMethod]
        public void SnapshotTest()
        {
            var stack = new Stack();
            new DeadLetterQueue(stack, "DeadLetterQueue");

            var template = Template.FromStack(stack);

            return Verifier.Verify(template.ToJSON());
        }
    }
}
```

## テストのヒント
<a name="testing-tips"></a>

テストはテストされるコードと同じ期間存続し、同じ頻度で読み取られて変更されることに注意してください。したがって、記述する方法を検討するために少し時間を割くことをお勧めします。

セットアップ行や一般的なアサーションをコピーして貼り付けないでください。代わりに、このロジックをフィクスチャまたはヘルパー関数にリファクタリングします。各テストが実際にテストする内容を反映した良い名前を使用してください。

1 回のテストでやりすぎないでください。テストでは、1 つの動作のみをテストすることが理想です。誤ってその動作を破った場合、1 つのテストのみが失敗してテストの名前が失敗した内容を知らせます。ただし、これは目指す理想には過ぎません。複数の動作をテストするテストをやむを得ず (または誤って) 記述する場合があります。既に説明した理由により、スナップショットテストには特にこの問題が発生しやすいため、控えめに使用してください。

# AWS SAM CLI を使用して AWS CDK アプリケーションをローカルでテストおよび構築する
<a name="testing-locally"></a>

AWS SAM CLI を使用して、AWS Cloud Development Kit (AWS CDK) を使用して定義されたサーバーレスアプリケーションをローカルでテストおよび構築できます。AWS SAM CLI は AWS CDK プロジェクト構造内で機能するため、ユーザーは AWS CDK アプリケーションの作成、変更、およびデプロイに引き続き「[AWS CDK CLI リファレンス](cli.md)」を使用できます。

AWS SAM の使用の詳細については、「*AWS サーバーレスアプリケーションモデル開発者ガイド*」の「[AWS SAM の使用を開始する](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started.html)」を参照してください。

**Topics**
+ [ローカルテストの開始方法](testing-locally-getting-started.md)
+ [AWS SAM を使用する AWS CDK アプリケーションのローカルでのテスト](testing-locally-with-sam-cli.md)
+ [AWS SAM を使用する AWS CDK アプリケーションの構築](testing-locally-build-with-sam-cli.md)

# ローカルテストの開始方法
<a name="testing-locally-getting-started"></a>

このトピックでは、AWS CDK アプリケーションで AWS SAM CLI を使用するために必要な事柄と、シンプルな AWS CDK アプリケーションを構築してローカルにテストするための手順を説明します。

## 前提条件
<a name="testing-locally-getting-started-prerequisites"></a>

ローカルでテストするには、AWS SAM CLI をインストールする必要があります。インストール手順については、「[AWS SAM CLI のインストール](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/getting_started.html)」を参照してください。

## AWS CDK アプリケーションの作成とローカルでのテスト
<a name="testing-locally-getting-started-tutorial"></a>

AWS SAM CLI を使用して AWS CDK アプリケーションをローカルにテストするには、Lambda 関数が含まれる AWS CDK アプリケーションがある必要があります。次のステップを使用して、Lambda 関数を使用する基本的な AWS CDK アプリケーションを作成します。詳細については、「*AWS Cloud Development Kit (AWS CDK) 開発者ガイド*」の「[AWS CDK を使用して、サーバーレスアプリケーションを作成する](https://docs.aws.amazon.com/cdk/latest/guide/serverless_example.html)」を参照してください。<a name="testing-locally-getting-started-tutorial-init"></a>

 **ステップ 1: AWS CDK アプリケーションを作成する**   
このチュートリアルでは、TypeScript を使用するAWS CDK アプリケーションを初期化します。  
実行するコマンド:  

```
$ mkdir cdk-sam-example
$ cd cdk-sam-example
$ cdk init app --language typescript
```<a name="testing-locally-getting-started-tutorial-lambda"></a>

 **ステップ 2: アプリケーションに Lambda 関数を追加する**   
`lib/cdk-sam-example-stack.ts` のコードを、以下のコードに置き換えます。  

```
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';

export class CdkSamExampleStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    new lambda.Function(this, 'MyFunction', {
      runtime: lambda.Runtime.PYTHON_3_12,
      handler: 'app.lambda_handler',
      code: lambda.Code.fromAsset('./my_function'),
    });
  }
}
```<a name="testing-locally-getting-started-tutorial-code"></a>

 **ステップ 3: Lambda 関数コードを追加する**   
`my_function` という名前のディレクトリを作成します。そのディレクトリに `app.py` という名前のファイルを作成します。  
実行するコマンド:  

**Example**  

```
$ mkdir my_function
$ cd my_function
$ touch app.py
```

```
$ mkdir my_function
$ cd my_function
$ type nul > app.py
```

```
$ mkdir my_function
$ cd my_function
$ New-Item -Path "app.py”
```
次のコードを `app.py` に追加します。

```
def lambda_handler(event, context):
    return "Hello from SAM and the CDK!"
```<a name="testing-locally-getting-started-tutorial-function"></a>

 **ステップ 4: Lambda 関数をテストする**   
AWS CDK アプリケーションで定義されている Lambda 関数は、 AWS SAM CLI を使用してローカルに呼び出すことができます。これを行うには、呼び出す関数のコンストラクト識別子と、合成した AWS CloudFormation テンプレートへのパスが必要です。  
`lib` ディレクトリに戻って、次のコマンドを実行します。  

```
$  cd ..
```
 **実行するコマンド:**   

```
$  cdk synth --no-staging
```

```
$  sam local invoke MyFunction --no-event -t ./cdk.out/CdkSamExampleStack.template.json
```
 **出力例:**   

```
Invoking app.lambda_handler (python3.9)

START RequestId: 5434c093-7182-4012-9b06-635011cac4f2 Version: $LATEST
"Hello from SAM and the CDK!"
END RequestId: 5434c093-7182-4012-9b06-635011cac4f2
REPORT RequestId: 5434c093-7182-4012-9b06-635011cac4f2	Init Duration: 0.32 ms	Duration: 177.47 ms	Billed Duration: 178 ms	Memory Size: 128 MB	Max Memory Used: 128 MB
```

# AWS SAM を使用する AWS CDK アプリケーションのローカルでのテスト
<a name="testing-locally-with-sam-cli"></a>

AWS SAM CLI を使用して、AWS CDK アプリケーションのプロジェクトルートディレクトリから以下のコマンドを実行することで、AWS CDK アプリケーションをローカルにテストできます。
+  ` [sam local invoke](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-invoke.html) ` 
+  ` [sam local start-api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-start-api.html) ` 
+  ` [sam local start-lambda](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-start-lambda.html) ` 

AWS CDK アプリケーションで、`sam local` コマンドのいずれかを実行する前に、`cdk synth` を実行する必要があります。

`sam local invoke` の実行時、呼び出す関数のコンストラクト識別子と、合成した AWS CloudFormation テンプレートへのパスが必要です。アプリケーションでネストされたスタックを使用する場合、名前の競合を解決するには、関数が定義されているスタックの名前が必要です。

 **使用方法**:   

```
# Invoke the function FUNCTION_IDENTIFIER declared in the stack STACK_NAME
$  sam local invoke <OPTIONS> <STACK_NAME/FUNCTION_IDENTIFIER>

# Start all APIs declared in the AWS CDK application
$  sam local start-api -t <./cdk.out/CdkSamExampleStack.template.json> <OPTIONS>

# Start a local endpoint that emulates AWS Lambda
$  sam local start-lambda -t <./cdk.out/CdkSamExampleStack.template.json> <OPTIONS>
```

## 例
<a name="testing-cdk-applications-examples"></a>

以下のサンプルで宣言されているスタックと関数について考えてみましょう。

```
app = new HelloCdkStack(app, "HelloCdkStack",
   ...
)
class HelloCdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    ...
    new lambda.Function(this, 'MyFunction', {
        ...
    });

    new HelloCdkNestedStack(this, 'HelloNestedStack' ,{
        ...
    });
  }
}

class HelloCdkNestedStack extends cdk.NestedStack {
  constructor(scope: Construct, id: string, props?: cdk.NestedStackProps) {
    ...
    new lambda.Function(this, 'MyFunction', {
        ...
    });
    new lambda.Function(this, 'MyNestedFunction', {
        ...
    });
  }
}
```

以下のコマンドは、上記の例で定義されている Lambda 関数をローカルに呼び出します。

```
# Invoke MyFunction from the HelloCdkStack
$ sam local invoke -t <./cdk.out/HelloCdkStack.template.json> <MyFunction>
```

```
# Invoke MyNestedFunction from the HelloCdkNestedStack
$ sam local invoke -t <./cdk.out/HelloCdkStack.template.json> <MyNestedFunction>
```

```
# Invoke MyFunction from the HelloCdkNestedStack
$ sam local invoke -t <./cdk.out/HelloCdkStack.template.json> <HelloNestedStack/MyFunction>
```

# AWS SAM を使用する AWS CDK アプリケーションの構築
<a name="testing-locally-build-with-sam-cli"></a>

AWS SAM CLI は、` [sam build](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-build.html) ` により AWS CDK アプリケーションで定義された Lambda 関数とレイヤーを構築するためのサポートを提供します。

zip Artifact を使用する Lambda 関数の場合は、`sam local` コマンドを実行する前に `cdk synth` を実行してください。`sam build` は、必須ではありません。

AWS CDK アプリケーションでイメージタイプの関数を使用する場合は、`sam local` コマンドを実行する前に `cdk synth` を実行し、`sam build` を実行してください。`sam build` を実行すると、AWS SAM は、` [NodejsFunction](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html) ` などのランタイム固有の構造を使用する Lambda 関数またはレイヤーを構築しません。`sam build` は[バンドルされたアセット](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.BundlingOptions.html)をサポートしていません。

## 例
<a name="testing-locally-build-with-sam-cli-examples"></a>

AWS CDK プロジェクトルートディレクトリから以下のコマンドを実行すると、アプリケーションが構築されます。

```
$ sam build -t <./cdk.out/CdkSamExampleStack.template.json>
```