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:
Resources
only have the CDKMetadata
entry. This is about to change.
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:
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 we 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 synth
output 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.
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.
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
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.
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.
The post has been cross-posted to Medium