継続は力なり

タイトル通り定期的な更新を心掛けるブログです。

『Jest』での『Fine-grained assertions test』と『Validation tests』の実践

タダです.

AWS CDK のテスト手法で前回は「Snapshot tests」を学んだので,今回は「Fine-grained assertions test」と「Validation tests」を学んでいきます.

「Fine-grained assertions test」と「Validation tests」の意義

  • Fine-grained assertions test」とは,CDKで作成したテンプレートの一部をチェックし意図した設定になっているかをテストするための手法です.
    • 例えば,SQS の場合は expect(stack).toHaveResource('AWS::SQS::Queue', {プロパティ}) の記述で検証します.
  • Validation tests」とは,特定のリソースに対する適切な Construct が定義されているかを検証する手法です.一般的なユニットテストにあたります.

上記の2つのテストを前回の SQS リソースを使って実践します.

sadayoshi-tada.hatenablog.com

「Fine-grained assertions test」 の実践

インフラソースコードの準備

以下のSQS デッドレターキューのソースコードに対して「Fine-grained assertions test」を行います.//でコメントしている箇所のみを変更したとします.

import cloudwatch = require('@aws-cdk/aws-cloudwatch');
import sqs = require('@aws-cdk/aws-sqs');
import { Construct, Duration } from '@aws-cdk/core';

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

    // デッドレターキューの保存期間が14日間の定義
    constructor(scope: Construct, id: string) {
        super(scope,id , {
            retentionPeriod: Duration.days(14)
        });
    // SQS の CloudWatch メトリクスで ApproximateNumberOfMessagesVisible を定義
        this.messagesInQueueAlarm = new cloudwatch.Alarm(this, 'Alarm', {
            alarmDescription: 'There are messages in the Dead Letter Queue',
            evaluationPeriods: 1,
            threshold: 1,
            metric: this.metricApproximateNumberOfMessagesVisible(),
            period: Duration.minutes(1),
        });
    }
}

テストコードの準備

2つのテストコードを用意します.1つ目はデッドレターキューの CloudWatch メトリクス ApproximateNumberOfMessagesVisible が設定されているかのテストで,2つ目はデッドレターキューの保存期間が14日間かのテストになります.

import { Stack } from '@aws-cdk/core';
import '@aws-cdk/assert/jest';

import dlq = require('../lib/dead-letter-queue');

test('dlq creates an alarm', () => {
    const stack = new Stack();

    new dlq.DeadLetterQueue(stack, 'DLQ');

    expect(stack).toHaveResource('AWS::CloudWatch::Alarm', {
        MetricName: "ApproximateNumberOfMessagesVisible",
        Namespace: "AWS/SQS",
        Dimensions: [
        {
            Name: "QueueName",
            Value: { "Fn::GetAtt": [ "DLQ581697C4", "QueueName" ] }
        }
        ],
    });
});

test('dlq has maximum retention period', () => {
    const stack = new Stack();

    new dlq.DeadLetterQueue(stack, 'DLQ');

    expect(stack).toHaveResource('AWS::SQS::Queue', {
        MessageRetentionPeriod: 1209600
    });
});

テストコードの実行

テストコードを実行してみると2つとも意図した設定の通りであったためテストが通りました.変更を加えた箇所のみをテストをしたい場合に「Fine-grained assertions test」が有効でしょう.

>> npm run build && npm test

> jest-handson@0.1.0 build /XXX/XXX/awscdk-handson/jest-handson
> tsc


> jest-handson@0.1.0 test /XXX/XXX/awscdk-handson/jest-handson
> jest

 PASS  test/dead-letter-queue.test.ts
  ✓ dlq creates an alarm (69ms)
  ✓ dlq has maximum retention period (30ms)

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

「Validation tests」の実践

インフラソースコードの準備

次に以下のコードに対して「Validation tests」を実践します.SQS のデッドレターキューの保存期間を Construct の Props に渡された値で設定可能なのが最長14日間であるよう定義しています.

import { IAlarm, Alarm } from '@aws-cdk/aws-cloudwatch'
import { Queue } from '@aws-cdk/aws-sqs'
import { Construct, Duration } from '@aws-cdk/core'

export interface DeadLetterQueueProps {
    /**
     * The amount of days messages will live in the dead letter queue
     *
     * Cannot exceed 14 days.
     *
     * @default 14
     */
    retentionDays?: number;
}

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

  constructor(scope: Construct, id: string, props: DeadLetterQueueProps = {}) {
    if (props.retentionDays !== undefined && props.retentionDays > 14) {
      throw new Error('retentionDays may not exceed 14 days');
    }

    super(scope, id, {
        // Given retention period or maximum
        retentionPeriod: Duration.days(props.retentionDays || 14)
    })

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

テストコードの準備

このテストは CloudFormation テンプレートでリソースにデッドレターキューの保存期間の値を保持しているか,デッドレターキューの保存期間が14日間を超えた場合にエラーが出るかをテストします.

import { Stack } from '@aws-cdk/core';
import '@aws-cdk/assert/jest';

import dlq = require('../lib/dead-letter-queue');

test('retention period can be configured', () => {
    const stack = new Stack();

    new dlq.DeadLetterQueue(stack, 'DLQ', {
        retentionDays: 7
    })
    expect(stack).toHaveResource('AWS::SQS::Queue', {
        MessageRetentionPeriod: 604800,
    })
})

test('configurable retention period cannot exceed 14 days', () => {
    const stack = new Stack()
    expect(() => {
        new dlq.DeadLetterQueue(stack, 'DLQ', {retentionDays: 15})
    }).toThrowError(/retentionDays may not exceed 14 days/)
})

テストコードの実行

テストコードを実行してみると以下のように2つのテストとも通ったことを確認しました.

>> npm run build && npm test

> jest-handson@0.1.0 build /XXX/XXX/awscdk-handson/jest-handson
> tsc


> jest-handson@0.1.0 test /XXX/XXX/awscdk-handson/jest-handson
> jest

 PASS  test/dead-letter-queue.test.ts
  ✓ retention period can be configured (87ms)
  ✓ configurable retention period cannot exceed 14 days (2ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.753s, estimated 5s
Ran all test suites.

まとめ

Fine-grained assertions test」と「Validation tests」の意義とテストコードを実践しました.目的に応じて3種類のテストを使い分けて,質の高いインフラのコード開発とデプロイを実践していきたいですね.

関連記事

AWS CDK の他の記事もぜひ!

sadayoshi-tada.hatenablog.com

sadayoshi-tada.hatenablog.com

sadayoshi-tada.hatenablog.com

sadayoshi-tada.hatenablog.com