JestのSnapshot Serializerを利用してAWS CDKのスナップショットテストのアセットの差分を無視する

こんにちは。サービスグループの武田です。AWS CDKのスナップショットテストのアセットの差分を、Snapshot Serializerを利用して無視する方法を試してみました。
2021.03.18

こんにちは。サービスグループの武田です。

先日、AWS CDKのスナップショットテストで発生するアセットの差分を無視する方法を紹介しました。

これを見た同僚から、別の方法もあるよ!と教えてもらったので調べてやってみました。

Snapshot Serializerを利用する

AWS CDKのユニットテストはライブラリとしてJestが利用されています。実はJestにはシリアライズ用のモジュールを追加する機能があり、これを利用することでハッシュ値の出力を操作できます。

モジュールの追加方法は次の2種類が提供されています。

  1. jest.configsnapshotSerializersに指定する
  2. 各テストケースでexpect.addSnapshotSerializerを呼んで追加する

1は複数のテストケースで共有したい場合に便利です。2はテストケースごとにモジュールを切り替えたい場合などに便利です。今回は設定ファイルで設定する1の方法を試してみました。

ベースとなるプロジェクトは前述のエントリで作成したものを利用します。実際に試してみたい方は前述のエントリを参考にしてみてください。

やってみた

まずはモジュールを定義します。

test/snapshot-plugin.ts

module.exports = {
  test: (val: any) => typeof val === 'string',
  serialize: (val: any) => {
    return `"${val.replace(/AssetParameters([A-Fa-f0-9]{64})(\w+)|(\w+) (\w+) for asset\s?(version)?\s?"([A-Fa-f0-9]{64})"/, '[HASH REMOVED]')}"`;
  },
};

このモジュールを使用するように、設定ファイルを編集します。

jest.config.js

module.exports = {
  roots: ['<rootDir>/test'],
  testMatch: ['**/*.test.ts'],
  transform: {
    '^.+\\.tsx?$': 'ts-jest'
  },
  snapshotSerializers: ['<rootDir>/test/snapshot-plugin.ts']
};

最後にテストケースを次のように変更します。すっきりしていますね。

test/cdk-ignore-test-assets.test.ts

import '@aws-cdk/assert/jest';

import { SynthUtils } from '@aws-cdk/assert';
import * as cdk from '@aws-cdk/core';

import * as CdkIgnoreTestAssets from '../lib/cdk-ignore-test-assets-stack';

test('Snapshot Test', () => {
  const app = new cdk.App();
  const stack = new CdkIgnoreTestAssets.CdkIgnoreTestAssetsStack(app, 'cdkIgnoreTestAssetsStack');

  expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot();
});

モジュールの有無によるスナップショットの差分は次のようになりました。

@@ -1,17 +1,17 @@
    Object {
    "Parameters": Object {
-     "AssetParametersbca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496ArtifactHashFAEC5CD3": Object {
-       "Description": "Artifact hash for asset \"bca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496\"",
+     "[HASH REMOVED]": Object {
+       "Description": "[HASH REMOVED]",
        "Type": "String",
        },
-     "AssetParametersbca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496S3Bucket170AB6D6": Object {
-       "Description": "S3 bucket for asset \"bca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496\"",
+     "[HASH REMOVED]": Object {
+       "Description": "[HASH REMOVED]",
        "Type": "String",
        },
-     "AssetParametersbca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496S3VersionKeyF774BA74": Object {
-       "Description": "S3 key for asset version \"bca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496\"",
+     "[HASH REMOVED]": Object {
+       "Description": "[HASH REMOVED]",
        "Type": "String",
        },
    },
    "Resources": Object {
        "LambdaFunctionBF21E41F": Object {
@@ -19,11 +19,11 @@
            "LambdaFunctionServiceRoleC555A460",
        ],
        "Properties": Object {
            "Code": Object {
            "S3Bucket": Object {
-             "Ref": "AssetParametersbca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496S3Bucket170AB6D6",
+             "Ref": "[HASH REMOVED]",
            },
            "S3Key": Object {
                "Fn::Join": Array [
                "",
                Array [
@@ -32,11 +32,11 @@
                        0,
                        Object {
                        "Fn::Split": Array [
                            "||",
                            Object {
-                           "Ref": "AssetParametersbca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496S3VersionKeyF774BA74",
+                           "Ref": "[HASH REMOVED]",
                            },
                        ],
                        },
                    ],
                    },
@@ -45,11 +45,11 @@
                        1,
                        Object {
                        "Fn::Split": Array [
                            "||",
                            Object {
-                           "Ref": "AssetParametersbca73d9caf09b131f3b33cc87844d2707c6ac9c9abc18644c670eec939b26496S3VersionKeyF774BA74",
+                           "Ref": "[HASH REMOVED]",
                            },
                        ],
                        },
                    ],
                    },

    17 |   const stack = new CdkIgnoreTestAssets.CdkIgnoreTestAssetsStack(app, 'cdkIgnoreTestAssetsStack');
    18 |
> 19 |   expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot();
        |                                              ^
    20 | });
    21 |

    at Object.<anonymous>.test (test/cdk-ignore-test-assets.test.ts:19:46)

ハッシュ値の部分が[HASH REMOVED]に置き換わってますね!

まとめ

JestのSnapshot Serializerというものを知らなかったので教えてくれた同僚には感謝です。jest.configでモジュールを指定すれば、各テストケースで対応する必要もないため楽ですね。ケースバイケースで使い分けていきましょう。

参考サイト