AWS CDKでリソースの構築とテストコードの書き方について

はじめに

最近はAWS CDKでリソースを構築することが多いのですが、AWS CDKは一般的なプログラミング言語AWSのリソースを定義するものなので、テストコードを書くことができます。そのテストコードの書き方についてまとめてみます。

前提条件

本記事は、以下の言語とAWS CDKのバージョンについて記しています。

  • TypeScript
  • AWS CDK 2.118.0

公式ドキュメント

AWS CDKの公式ドキュメントにテストについて記されたページがあるので目を通します。が、機械翻訳での提供なので、英語版を読んだ方が良いかもしれません。

docs.aws.amazon.com

テストのカテゴリ

上記公式ドキュメントに記されていますが、テストには以下2つのカテゴリがあるようです。

  • きめ細やかなアサーション(Fine-grained assertions)
    • AWS CDKが生成するCloudFormationの各種設定値が期待通りに生成されているかテストする
  • スナップショット(Snapshot test)
    • AWS CDKが生成するCloudFormationのテンプレートを以前のものと比較し、差分があるかをテストする

開発中は「きめ細やかなアサーション(Fine-grained assertions)」でテストを書いて、本番運用後は「スナップショット(Snapshot test)」を組み合わせる感じかなと考えました。

今回は「きめ細やかなアサーション(Fine-grained assertions)」の書き方について記します。

実装

cdk initにてプロジェクトを生成すると以下のようなサンプルテストコード込みで生成されるので、それを拡張するようにします。

// import * as cdk from 'aws-cdk-lib';
// import { Template } from 'aws-cdk-lib/assertions';
// import * as Cdk from '../lib/cdk-stack';

// example test. To run these tests, uncomment this file along with the
// example resource in lib/cdk-stack.ts
test('SQS Queue Created', () => {
//   const app = new cdk.App();
//     // WHEN
//   const stack = new Cdk.CdkStack(app, 'MyTestStack');
//     // THEN
//   const template = Template.fromStack(stack);

//   template.hasResourceProperties('AWS::SQS::Queue', {
//     VisibilityTimeout: 300
//   });
});

template変数を生成するまではいわゆるおまじないなので、ある程度コピペで。stack変数を生成するnew Cdk.CdkStack(app, 'MyTestStack');は適宜自分が生成したいStackに合わせて修正するぐらいです。

AWS CDKはCloudFormationを生成するので、CloudFormationの値が想定値と一致しているかどうかをテストするのが基本的な戦略になるかなと思います。AWS CDKのaws-cdk-lib.assertionsモジュールにて、各種Matcherの説明があるのでこれがテストコードの実装の拠り所になるかなと思います。

docs.aws.amazon.com

個人的には、以下のMatcherを使うことが多いです。

  • resourceCountIs
    • 対象のリソースが想定する数作成されるか
  • hasResourceProperties
    • 対象のリソースのプロパティが想定している値であるか
  • hasResource
    • 対象のリソースのプロパティ以外の設定(DeletionPolicyなど)の値が想定しているものか

繰り返しになりますが、AWS CDKはCloudFormationを生成するので、各種検証する値はCloudFormationのドキュメントも合わせて確認することになるかなと思います。

docs.aws.amazon.com

利用するサービスに対してCloudFormationの知識がないとテストも書きにくいと思います。この辺がちょっとハードルが高いかなと思いますが、一回ですべてを網羅するのは難しいので徐々に書き足す形が良いのかなと思っています。

なお、AWSの各種サービスは日々バージョンアップが行われ、AWS CDK自身も頻繁にバージョンアップされています。以下のページを見ると、1ヶ月に4回以上はザラにアップデートされています。

www.npmjs.com

CloudFormationのドキュメントにはあるのにAWS CDKにはない場合にはAWS CDKのバージョンアップを実施するとサクッと解決することが多いかなと思います。

実行

実行はnpm run testで実行できます。実行後、失敗するとどこで失敗したかがわかるように表示されます。ここらへんはJtestの機能です。

% npm run test

> cdk@0.1.0 test
> jest

 FAIL  test/cdk.test.ts
  ✕ ECRが1つ作成されること (82 ms)

  ● ECRが1つ作成されること

    Template has 1 resources with type AWS::ECR::Repository, but none match as expected.
    The 1 closest matches:
    Repository22E53BBD :: {
      "DeletionPolicy": "Delete",
      "Properties": {
    !!   Expected false but received true
        "EmptyOnDelete": true,
        "RepositoryName": "my-ruby-app"
      },
      "Type": "AWS::ECR::Repository",
      "UpdateReplacePolicy": "Delete"
    }

       9 |
      10 |   template.resourceCountIs('AWS::ECR::Repository', 1);
    > 11 |   template.hasResourceProperties('AWS::ECR::Repository', {
         |            ^
      12 |     RepositoryName: 'my-ruby-app',
      13 |     EmptyOnDelete: false,
      14 |   });

      at Template.hasResourceProperties (node_modules/aws-cdk-lib/assertions/lib/template.js:1:3101)
      at Object.<anonymous> (test/cdk.test.ts:11:12)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.696 s, estimated 2 s

成功すると、以下のように表示されます。

% npm run test            

> cdk@0.1.0 test
> jest

 PASS  test/cdk.test.ts
  ✓ ECRが1つ作成されること (83 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.712 s
Ran all test suites.

考察

AWS CDK自身は1ヶ月に複数回バージョンアップされています。クラウドの更新頻度を踏まえるとそれぐらいのバージョンアップは必然かなと思います。そのバージョンアップに追随するためにはテストコードを書くのは自然な発想と考えています。世の中、AWS CDKを使ってリソースを作ることは書かれていますが、テストコードの書き方は書かれていなかったので今回記事化しました。

実際には、AWS CDK、CloudFormation、Jestの知識がいるのでテストを書くとなるとハードルが高いなと感じます。ですが、一度に書き上げるのではなく徐々に作り上げていくとよいのかなと思いました。

ここで書いたのはあくまで基本的な記述方法で、上記のマニュアルにもいろんな検証方法が掲載されています。それらも実際の利用シーンとあわせて実装してみることが今後の課題です。

また、カバレッジを取得することはできないのかなと思ったりもしました。AWS CDKではそんなに複雑なことをしないケースがほとんどですのであまりニーズはないのかもしれません。