こんにちは。 エキサイト株式会社で内定者アルバイトをしている平石です。
今回は、AWS CDKで正しくリソースが定義されているかをテストするための方法をご紹介します。
前の記事
2種類のテスト
AWS CDKは通常のプログラミング言語で記述できるということもあって、テストが書けるというのも魅力の一つなようです。
AWS CDKでは2種類のテストがサポートされています。
一つは、CDKで記述したコードから生成されたAWS CloudFormationテンプレートの特定の要素をテストするためのFine-grained assertionsと呼ばれるものです。
あるリソースが存在するか?いくつあるか?特定のプロパティの値が正しくセットされているか?といったテストができ、テスト駆動開発をする上で便利なものです。こちらの方がよく使われるようです。
もう一つは、生成されたAWS CloudFormationテンプレートが以前(直前の成功したテスト)に生成されたテンプレートと一致するかをテストするためのSnapshot testsと呼ばれるものです。
こちらは、リソースの追加や削除等の変更を伴うと失敗してしまうので、通常の開発時に行うことはほとんどありません。
では、このテストをいつ使用するかというと、コードをリファクタリングする時です。
すなわち、リファクタリング後のコードが生成するリソースは、リファクタリング前のコードが生成するリソースと全く同じであるかをチェックできます。
実際に簡単な例で試してみる
Fine-grained assertions
では、まずはFine-grained assertionsを簡単な例でやってみましょう。
前提となるコードは、以下のVPCとサブネットおよびEC2インスタンスを生成するコードです。
このコードの詳細については、以下の記事をご覧ください。
package com.myorg; import software.amazon.awscdk.CfnTag; import software.amazon.awscdk.Duration; import software.amazon.awscdk.services.ec2.CfnInternetGateway; import software.amazon.awscdk.services.ec2.CfnVPCGatewayAttachment; import software.amazon.awscdk.services.ec2.IMachineImage; import software.amazon.awscdk.services.ec2.Instance; import software.amazon.awscdk.services.ec2.InstanceClass; import software.amazon.awscdk.services.ec2.InstanceSize; import software.amazon.awscdk.services.ec2.InstanceType; import software.amazon.awscdk.services.ec2.MachineImage; import software.amazon.awscdk.services.ec2.Peer; import software.amazon.awscdk.services.ec2.Port; import software.amazon.awscdk.services.ec2.PrivateSubnet; import software.amazon.awscdk.services.ec2.PublicSubnet; import software.amazon.awscdk.services.ec2.SecurityGroup; import software.amazon.awscdk.services.ec2.SubnetSelection; import software.amazon.awscdk.services.ec2.Vpc; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; import java.util.List; public class MyProjectStack extends Stack { public MyProjectStack(final Construct scope, final String id) { this(scope, id, null); } public MyProjectStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); Vpc vpc = Vpc.Builder.create(this, "SampleVPC") .vpcName("sample-vpc") .cidr("cidr") .subnetConfiguration(List.of()) .build(); // Availability zone: ap-northeast-1a PrivateSubnet privateSubnetA = PrivateSubnet.Builder.create(this, "PrivateSubnetA") .availabilityZone("ap-northeast-1a") .vpcId(vpc.getVpcId()) .cidrBlock("cidr") .build(); PublicSubnet publicSubnetA = PublicSubnet.Builder.create(this, "PublicSubnetA") .availabilityZone("ap-northeast-1a") .vpcId(vpc.getVpcId()) .cidrBlock("cidr") .build(); // Availability zone: ap-northeast-1c PrivateSubnet privateSubnetB = PrivateSubnet.Builder.create(this, "PrivateSubnetB") .availabilityZone("ap-northeast-1c") .vpcId(vpc.getVpcId()) .cidrBlock("cidr") .build(); PublicSubnet publicSubnetB = PublicSubnet.Builder.create(this, "PublicSubnetB") .availabilityZone("ap-northeast-1c") .vpcId(vpc.getVpcId()) .cidrBlock("cidr") .build(); // NAT gatewayの追加 publicSubnetA.addNatGateway(); publicSubnetB.addNatGateway(); // Internet Gateway CfnInternetGateway internetGateway = CfnInternetGateway.Builder.create(this, "InternetGateway") .tags(List.of(CfnTag.builder() .key("Name") .value("sample-internet-gateway") .build())) .build(); // Internet gatewayをVPCにattachする CfnVPCGatewayAttachment.Builder.create(this, "VpcGateAwayAttachment") .vpcId(vpc.getVpcId()) .internetGatewayId(internetGateway.getAttrInternetGatewayId()) .build(); SecurityGroup securityGroup = SecurityGroup.Builder.create(this, "SampleSecurityGroup") .securityGroupName("sample-security-group") .vpc(vpc) .build(); IMachineImage machineImage = MachineImage.latestAmazonLinux(); Instance instance01 = Instance.Builder.create(this, "ec2-instance-01") .instanceName("ec2-instance-01") .vpcSubnets(SubnetSelection.builder().subnets(List.of(privateSubnetA)).build()) .machineImage(machineImage) .vpc(vpc) .availabilityZone("ap-northeast-1a") .instanceType(InstanceType.of(InstanceClass.T2, InstanceSize.SMALL)) .securityGroup(securityGroup) .build(); Instance instance02 = Instance.Builder.create(this, "ec2-instance-02") .instanceName("ec2-instance-02") .vpcSubnets(SubnetSelection.builder().subnets(List.of(privateSubnetB)).build()) .machineImage(machineImage) .vpc(vpc) .availabilityZone("ap-northeast-1c") .instanceType(InstanceType.of(InstanceClass.T2, InstanceSize.SMALL)) .securityGroup(securityGroup) .build(); // Load Balancer // Load Balancer用のSecurity Group SecurityGroup securityGroupEC2LB = SecurityGroup.Builder.create(this, "SecurityGroupEC2LoadBalancer") .securityGroupName("ec2-lb-security-group") .vpc(vpc) .description("security-group-of-ec2-load-balancer") .allowAllOutbound(true) .build(); securityGroupEC2LB.addIngressRule(Peer.ipv4("0.0.0.0/0"), Port.tcp(80)); ApplicationLoadBalancer applicationLoadBalancer = ApplicationLoadBalancer.Builder.create(this, "SampleEC2ApplicationLoadBalancer") .loadBalancerName("sample-ec2-load-balancer") .deletionProtection(false) .vpc(vpc) .vpcSubnets(SubnetSelection.builder().subnets(List.of(privateSubnetA, privateSubnetB)).build()) .securityGroup(securityGroupEC2LB) .ipAddressType(IpAddressType.IPV4) .idleTimeout(Duration.seconds(120)) .http2Enabled(true) .build(); ApplicationTargetGroup ec2TargetGroup = ApplicationTargetGroup.Builder.create(this, "EC2TargetGroup") .targetGroupName("ec2-target-group") .targetType(TargetType.INSTANCE) .port(80) .vpc(vpc) .targets(List.of(new InstanceTarget(instance01, 80), new InstanceTarget(instance02, 80))) .protocol(ApplicationProtocol.HTTP) .protocolVersion(ApplicationProtocolVersion.HTTP1) .healthCheck(HealthCheck.builder() .enabled(true) .path("/") .healthyThresholdCount(3) .unhealthyThresholdCount(2) .interval(Duration.seconds(60)) .timeout(Duration.seconds(5)) .protocol(Protocol.HTTP) .port("80") .build()) .build(); ApplicationListener applicationListener = ApplicationListener.Builder.create(this, "ApplicationListener") .loadBalancer(applicationLoadBalancer) .protocol(ApplicationProtocol.HTTP) .port(80) .defaultAction(ListenerAction.forward(List.of(ec2TargetGroup))) .build(); } }
なお、コード中の"cidr"
については、ご自身が使用されているものに変更してください。
では、このコードに対してEC2インスタンスが2個生成されることと、指定したプロパティを持つVPCが生成されることをテストしましょう。
自動生成されるテストのためのファイル<プロジェクト名>Test.java
を以下のように書き換えます。
package com.myorg; import org.junit.jupiter.api.Test; import software.amazon.awscdk.App; import software.amazon.awscdk.assertions.Template; import java.util.Collections; import java.util.Map; public class MyProjectTest { @Test public void testStack() { App app = new App(); MyProjectStack stack = new MyProjectStack(app, "test"); Template template = Template.fromStack(stack); template.resourceCountIs("AWS::EC2::Instance", 2); template.hasResourceProperties("AWS::EC2::VPC", Map.of( "CidrBlock", "cidr", "Tags", Collections.singletonList(Map.of( "Key", "Name", "Value", "sample-vpc")) )); } }
testStack
メソッドの最初の3行で、テストのためのCloudFormationのテンプレートを準備しています。
おまじないのようなものだと思ってください...
次の行では、EC2インスタンスがちょうど2個作成されていることをテストしています。resourceCountIs
は第一引数に対象となるリソースの種類を文字列で、第二引数にカウントを指定します。
第一引数で使用できる文字列はCloudFormationのテンプレートで使用されているものと同じです。
詳細はこちらを参照してください。
AWS リソースおよびプロパティタイプのリファレンス - AWS CloudFormation
さらに、次の行では指定したプロパティを持つリソースが生成されることをテストします。ここでは、hasResourceProperties
を使用しています。
このメソッドは、第一引数に対象となるリソースの種類を文字列で、第二引数に実際のプロパティをMap
で指定します。
ここまで記述できたら、実際にテストを実行してみましょう。
Javaでは以下のようなコマンドで実行できます。
mvn compile && mvn test
すると、以下のようにテストが成功します。
------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.myorg.MyProjectTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.938 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.023 s [INFO] Finished at: 2022-11-11T14:52:24+09:00 [INFO] ------------------------------------------------------------------------
では、ここで試しにテストコードのresourceCountIs
のカウントを3に変更してみるとどうなるでしょうか?
template.resourceCountIs("AWS::EC2::Instance", 3);
テストを実行してみます。
mvn compile && mvn test
------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.myorg.MyProjectTest Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 3.066 sec <<< FAILURE! com.myorg.MyProjectTest.testStack() Time elapsed: 3.065 sec <<< FAILURE! java.lang.RuntimeException: Error: Expected 3 resources of type AWS::EC2::Instance but found 2 @jsii/kernel.RuntimeError: Error: Expected 3 resources of type AWS::EC2::Instance but found 2 at Kernel._ensureSync (/private/var/folders/zq/xgjlrkpx6nd5lj0bq93n76v40000gr/T/jsii-java-runtime13468074528918510716/lib/program.js:8428:27) at Kernel.invoke (/private/var/folders/zq/xgjlrkpx6nd5lj0bq93n76v40000gr/T/jsii-java-runtime13468074528918510716/lib/program.js:7840:34) at KernelHost.processRequest (/private/var/folders/zq/xgjlrkpx6nd5lj0bq93n76v40000gr/T/jsii-java-runtime13468074528918510716/lib/program.js:11017:36) at KernelHost.run (/private/var/folders/zq/xgjlrkpx6nd5lj0bq93n76v40000gr/T/jsii-java-runtime13468074528918510716/lib/program.js:10977:22) at Immediate._onImmediate (/private/var/folders/zq/xgjlrkpx6nd5lj0bq93n76v40000gr/T/jsii-java-runtime13468074528918510716/lib/program.js:10978:46) at processImmediate (node:internal/timers:466:21) at software.amazon.jsii.JsiiRuntime.processErrorResponse(JsiiRuntime.java:127) at software.amazon.jsii.JsiiRuntime.requestResponse(JsiiRuntime.java:96) at software.amazon.jsii.JsiiClient.callMethod(JsiiClient.java:184) at software.amazon.jsii.Kernel.call(Kernel.java:52) at software.amazon.awscdk.assertions.Template.resourceCountIs(Template.java:259) at com.myorg.MyProjectTest.testStack(SampleDbTest.java:22) Results : Failed tests: com.myorg.MyProjectTest.testStack(): Error: Expected 3 resources of type AWS::EC2::Instance but found 2(..) Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.772 s [INFO] Finished at: 2022-11-11T15:01:09+09:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project sample-db: There are test failures. [ERROR] [ERROR] Please refer to /path/my-project/target/surefire-reports for the individual test results. [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
当然ですが、失敗しました。エラーメッセージを見てみるとjava.lang.RuntimeException: Error: Expected 3 resources of type AWS::EC2::Instance but found 2
となっており、AWS::EC2::Instanceは3つやゆうて期待されとんのに、2つしか見つからんやんけ!
と怒られています。
予想通りですね!
その他のテストのためのメソッドについては以下を参照してください。
Snapshot tests
次に、Snapshot testsを簡単な例でやってみましょう。
ちなみに、このSnapshot tests、公式ドキュメント(Testing constructs - AWS Cloud Development Kit (AWS CDK) v2)通りにやるとうまくいかないので注意してください。
まずは、必要な依存関係を追加します。pom.xml
に以下のような内容を追加します。
<dependency> <groupId>io.github.origin-energy</groupId> <artifactId>java-snapshot-testing-core</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>io.github.origin-energy</groupId> <artifactId>java-snapshot-testing-junit5</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.3</version> </dependency>
次に、snapshotのためのプロパティファイルを作成します。test/resources
ディレクトリを作成し、その中にsnapshot.properties
ファイルを作成しましょう。
serializer=au.com.origin.snapshots.serializers.ToStringSnapshotSerializer serializer.base64=au.com.origin.snapshots.serializers.Base64SnapshotSerializer serializer.json=au.com.origin.snapshots.serializers.JacksonSnapshotSerializer serializer.orderedJson=au.com.origin.snapshots.serializers.DeterministicJacksonSnapshotSerializer comparator=au.com.origin.snapshots.comparators.PlainTextEqualsComparator reporters=au.com.origin.snapshots.reporters.PlainTextSnapshotReporter snapshot-dir=__snapshots__ output-dir=src/test/java ci-env-var=CI
記述内容は基本的にこのままで問題ありません。
次に、スタックを定義します。
package com.myorg; import software.amazon.awscdk.services.s3.Bucket; import software.constructs.Construct; import software.amazon.awscdk.Stack; import software.amazon.awscdk.StackProps; public class SnapshotTestStack extends Stack { public SnapshotTestStack(final Construct scope, final String id) { this(scope, id, null); } public SnapshotTestStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); Bucket bucket = Bucket.Builder.create(this, "SampleBucket") .bucketName("sample-bucket-desunoyo") .versioned(true) .build(); } }
S3バケットを一個生成するだけのスタックです。
ちなみに、S3バケットの名前はグローバルで一意でなければならないので、sample-bucket-desunoyo
のままではデプロイは不可能ですのよ。
そのため、他と被らなそうな適当な名前に変更してください。
次に、テストコードを記述していきましょう。
自動生成されているコードを編集していきます。
package com.myorg; import au.com.origin.snapshots.Expect; import au.com.origin.snapshots.SnapshotVerifier; import au.com.origin.snapshots.annotations.SnapshotName; import au.com.origin.snapshots.config.PropertyResolvingSnapshotConfig; import au.com.origin.snapshots.junit5.SnapshotExtension; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import software.amazon.awscdk.App; import software.amazon.awscdk.assertions.Template; @ExtendWith({SnapshotExtension.class}) public class SnapshotTestTest { @SnapshotName("snapshot_test_shot") @Test public void testStack() throws NoSuchMethodException { App app = new App(); SnapshotTestStack stack = new SnapshotTestStack(app, "test"); Template template = Template.fromStack(stack); SnapshotVerifier snapshotVerifier = new SnapshotVerifier(new PropertyResolvingSnapshotConfig(), SnapshotTestTest.class); Expect expect = Expect.of(snapshotVerifier, SnapshotTestTest.class.getMethod("testStack")); expect.toMatchSnapshot(template.toJSON()); snapshotVerifier.validateSnapshots(); } }
snapshot-testの公式のGitHub(GitHub - origin-energy/java-snapshot-testing: Facebook style snapshot testing for JAVA Tests)には、
private Expect expect;
と書けば、expect
に依存性注入されるというように書かれていましたが、どうもうまくいかないので、GitHub - origin-energy/java-snapshot-testing: Facebook style snapshot testing for JAVA Testsを参考にexpect
を生成しています。
では、
mvn compile && mvn test
を実行してみましょう。
テスト部分のコンソールは以下のようになるはずです。
------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.myorg.SnapshotTestTest [main] INFO au.com.origin.snapshots.SnapshotFile - Snapshot File: src/test/java/com/myorg/__snapshots__/SnapshotTestTest.snap [main] WARN au.com.origin.snapshots.SnapshotContext - We detected you are running on a developer machine - if this is incorrect please override the isCI() method in SnapshotConfig Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.785 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.914 s [INFO] Finished at: 2022-11-11T16:30:25+09:00 [INFO] ------------------------------------------------------------------------
そして、__snapshots__
ディレクトリが作成され、その中にSnapshotTestTest.snap
ファイルが作成されていると思います。
これがスナップショットです。
では、CDKのコードを変化させてテストの結果がどうなるかを見ていきましょう。
Bucket bucket = Bucket.Builder.create(this, "SampleBucket") .bucketName("sample-bucket-desuno") .versioned(true) .build();
S3バケットの名前をsample-bucket-desunoyo
からsample-bucket-desuno
に変更しますの。
この状態で、
mvn compile && mvn test
を実行すると、
------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.myorg.SnapshotTestTest [main] INFO au.com.origin.snapshots.SnapshotFile - Snapshot File: src/test/java/com/myorg/__snapshots__/SnapshotTestTest.snap Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 2.707 sec <<< FAILURE! com.myorg.SnapshotTestTest.testStack() Time elapsed: 2.706 sec <<< FAILURE! au.com.origin.snapshots.exceptions.SnapshotMatchException: Error(s) matching snapshot(s) (1 failure) shadow.org.opentest4j.AssertionFailedError: Error on: snapshot_test_shot=[ {Resources={SampleBucket7F6F8160={Type=AWS::S3::Bucket, Properties={BucketName=sample-bucket-desuno, VersioningConfiguration={Status=Enabled}}, UpdateReplacePolicy=Retain, DeletionPolicy=Retain}}, 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.}]}}} ] Changed content at line 2: expecting: ["{Resources={SampleBucket7F6F8160={Type=AWS::S3::Bucket, Properties={BucketName=sample-bucket-desunoyo, VersioningConfiguration={Status=Enabled}}, UpdateReplacePolicy=Retain, DeletionPolicy=Retain}}, 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.}]}}}"] but was: ["{Resources={SampleBucket7F6F8160={Type=AWS::S3::Bucket, Properties={BucketName=sample-bucket-desuno, VersioningConfiguration={Status=Enabled}}, UpdateReplacePolicy=Retain, DeletionPolicy=Retain}}, 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.}]}}}"] at au.com.origin.snapshots.SnapshotContext.toMatchSnapshot(SnapshotContext.java:108) at au.com.origin.snapshots.Expect.toMatchSnapshot(Expect.java:56) at com.myorg.SnapshotTestTest.testStack(CdkEcsTest.java:29) Results : Failed tests: com.myorg.SnapshotTestTest.testStack(): Error(s) matching snapshot(s) (1 failure)(..) Tests run: 1, Failures: 1, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.841 s [INFO] Finished at: 2022-11-11T17:01:23+09:00 [INFO] ------------------------------------------------------------------------ [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project cdk-ecs: There are test failures. [ERROR] [ERROR] Please refer to /path/snapshot-test/target/surefire-reports for the individual test results. [ERROR] -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. [ERROR] Re-run Maven using the -X switch to enable full debug logging. [ERROR] [ERROR] For more information about the errors and possible solutions, please read the following articles: [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
上記のように表示されテストが失敗します(テンプレートが一致しないので当然ですが...)。
では、S3バケットの名前を元に戻し、以下のようにコードを変更するとどうなるでしょうか?
Bucket bucket = new Bucket(this, "SampleBucket", BucketProps.builder() .bucketName("sample-bucket-desunoyo") .versioned(true) .build());
CDKのコード自体は変更されていますが、生成されるCloudFormationのテンプレートは同じですので、
------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.myorg.SnapshotTestTest [main] INFO au.com.origin.snapshots.SnapshotFile - Snapshot File: src/test/java/com/myorg/__snapshots__/SnapshotTestTest.snap Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.702 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 3.743 s [INFO] Finished at: 2022-11-11T17:07:58+09:00 [INFO] ------------------------------------------------------------------------
テストは成功します。
ちなみに、上の例からも分かるようにテストに失敗したものはスナップショットとして保存されないので安心してください。
補足
snapshot testが失敗した時、エラーメッセージのスナップショットが見づらいと感じたかもしれません。
その場合には以下のような依存関係を追加し、
<dependency> <groupId>io.github.origin-energy</groupId> <artifactId>java-snapshot-testing-plugin-jackson</artifactId> <version>4.0.0</version> <scope>test</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.14.0</version> <scope>test</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.0</version> <scope>test</scope> </dependency>
テストコードを
expect.serializer("json").toMatchSnapshot(template.toJSON());
のように変更すると、コンソールでの出力がjson形式になり多少見やすくなります。
終わりに
今回は、AWS CDKでのテストの書き方の基礎をご紹介しました。
では、また次回。
次の記事