Tests for AWS CDK code

Axel Hodler
4 min readMar 3, 2021

--

Image by pisco on Pixabay

The AWS Cloud Development Kit (CDK) allows us to define our cloud infrastructure using code. Instead of clicking through the web UI, or AWS console as it is called, we use code. Code that can be checked into version control for collaboration, annotated with documentation and certainly for testing.

The following post focusses on the testing aspect.

If we choose TypeScript we can initialise a CDK project with the following command after having installed aws-cdk.

cdk init app --language typescript

The project ends up with a generated empty stack. As the comment states we have not defined anything yet.

As an addition an initial test case has been generated in the test directory of our project. It makes sure to test the fact our stack is empty. As defined above. Having the test directory and an initial test already tells us the CDK code can be tested and the intention of the framework authors to have tests.

We can run the tests via npm run test and get the following results:

How does this work? We have an assertion checking whether our template has resources. The template used for comparison is our Cloud Formation template. It is the output of cdk synth.

Without changing the generated CdkTestsStack, which has no resources defined yet, it looks as follows:

Part of the Cloud Formation template

Resources only have the CDKMetadata entry. This is about to change.

Building our stack

Let’s create a private ECR repository for our docker image. The image will later be used to run our application with AWS Fargate. The image is uploaded to the ECR repository and AWS Fargate will pick it up and run it.

We run npm i @aws-cdk/aws-ecr to get the required dependencies and extend the stack.

Running cdk synth again provides the following output:

Part of the Cloud Formation template

Now our repository is present in the resources. Our first resource! On to writing the test.

We assert our stack has the AWS::ECR::Repository resource with the RepositoryName property value test. The test passes.

Time to set up our ApplicationLoadBalancedFargateService. The construct is part of the @aws-cdk/aws-ecs-patterns. We reference our ECR repository in the taskImageOptions. Making sure the latest image is picked up by Fargate.

Aside from the image property did not provide other configuration. Thus the default values are used. We could write tests to be sure which values are used.

This both increases visibility of which values are used and makes sure we would learn about changes to the construct even when updating aws-ecs-patterns to a newer version. It allows us to learn if, for example, the DesiredCount default would ever change to two instances.

The next test asserts some of the default values.

We should only do this with properties that are important to us.

The test passes. Let’s try to test if the ApplicationLoadBalancedFargateService is using the latest image in our repository.

Here we can use haveResourceLike instead of haveResource . Else we would need to specify the values for Essential, LogConfiguration, Name and PortMappings too to allow for a proper comparison of ContainerDefinitions.

Using the CloudFormation JSON syntax makes the test pretty much unreadable. It has to be copied from the cdk synthoutput and pasted into the test code. It also gives us a glimpse of what writing CloudFormation templates would look like. In the future this cold be moved into a helper function.

Test driving changes

Say we are building a Spring Boot application. Thus the url to conduct the health check of our services via load balancer is /actuator/health. We need a way to configure it. It is a possible property in our AWS::ElasticLoadBalancingV2::TargetGroup resource.

Part of the Cloud Formation template

We write the following test

The new test fails. We can fix it by extending our stack and configuring the health check.

As a result cdk synth provides the new Cloud Formation template

Part of the Cloud Formation template

And all of our four tests pass

Sure, the health check will only work if we provide the default Spring Boot port of 8080 to the containerPort of taskImageOptions. But that is for another time.

Why would we want to test the CDK code?

There are plenty of projects around that don’t test their CDK code. They might be fully productive without it. The maintainers of the CDK project have tested the constructs to make sure they work. Thus we won’t have to. Still, there are some benefits to having them under test.

The tests can act as a sanity check. Are the constructs doing what I’m expecting them to do? As an early warning system if someone inadvertently removes something important. What if someone removes some critical configuration deemed unnecessary from their perspective? They can be used as documentation and specification for future developers or our own future selves. And of course as part of the test driven development workflow. We might not get as much design feedback on the constructs used from libraries but it will help to structure the way in which we work. Step by step.

--

--

Axel Hodler

Building things. Usually by writing code. www.hodler.co. Software Engineering @porschedigital