AWS · CDK · Testing
Architecture

Enhancing Your AWS CDK Projects with Testing

Experiences in Writing and Running Tests for AWS CDK Applications: Insights and Tips.

December 2025
·
6 min read
AWSCDKTesting
AI-generated

Automated unit and integration testing validates system components and increases confidence in your infrastructure code, but unfortunately this is a very neglected topic at CDK. But let's start at the beginning. A few weeks ago, we decided to start a new version of a CDK construct library in a project - this meant taking our CDK testing to a new level. We wanted to test each custom construct with multiple unit and integration tests. Here is an explanation of the difference between a unit test and an integration test.

Unit testing in the CDK focuses on testing the logic within CDK constructs, which are reusable components that define and configure cloud resources. These tests simply synthesize CDK code to a CloudFormation template without actually deploying any AWS resources. In short, AWS services are simply mocked up and we compare the generated template against our checks.

AWS CDK Testing Architecture

Integration testing, on the other hand, deploys the actual resources to an AWS account to ensure that the deployed resources work together correctly. These tests verify the behavior of the entire AWS application, including permissions, networking, and service integrations. Unlike unit testing, integration testing requires a real AWS account and uses tools such as the AWS SDK, AWS CLI, and custom resources to verify functionality in a real-world environment. We use the @aws-cdk/integ-tests-alpha module for testing in CDK.

Since we're just starting out with this new topic and wanted to see how other people are testing their infrastructure, we decided to do some research. Unfortunately there's a lack of good examples on the web and the same example is copied across many blog posts on the web. This situation is over now. After troubleshooting, testing, failing and gaining experience, we are now in a position to share our experiences and examples with you.

Unit Testing

"No, you don’t need to test every line of your CDK application" - Yan Cui

If you only declare and use official CDK constructs, we don't necessarily have to test. I would only test to ensure compliance (security and regulatory compliance), because as in the shift left principle, we can prevent deploying insecure resources through CDK testing. However, this should not be used as an alternative, just as an addition to check if you comply with security and regulatory frameworks. You should still use AWS Config and/or conformance packs that are rolled out organization-wide to check AWS resources against your compliance requirements.

But now let's start with an easy example. Since Security is everyone's job, we should test if all RDS Instances will be encrypted. This can be easily done like that:

template.allResourcesProperties("AWS::RDS::DBInstance", {
  StorageEncrypted: true,
});

In addition, we should use a custom KMS key to encrypt the RDS Instance and this key should be rotated regularly.

// KMS Key rotation Check
template.allResourcesProperties("AWS::KMS::Key", {
  EnableKeyRotation: true
});

// RDS Instance Check
template.allResourcesProperties("AWS::RDS::DBInstance", {
  StorageEncrypted: true,
  KmsKeyId: Match.anyValue(),
});

Unit tests like this could be implemented in any CDK project when using tools like projen - or whenever you create your own custom construct library. Imagine you have a platform team and a lot of application development teams, and you want to give them easily deployable infrastructure code that meets your company's security and regulatory frameworks.

Now let's take it a step further: we want to provide our application teams with constructs that expose databases or other resources according to our corporate specifications and legal requirements. This often requires custom resources. To find these in the synthesized stack, we need a little more configuration to specify the resources and check that the references are set correctly.

In the following example, we search for a KMS key using the properties in the synthesised template and check that the reference is set correctly in the secret:

function logicalIdFromResource(resource: any) {
  try {
    const resKeys = Object.keys(resource);
    if (resKeys.length === 0) throw new Error("No Resource found.");
    if (resKeys.length !== 1) throw new Error("Resource is not unique.");
    const [logicalId] = resKeys;
    return logicalId;
  } catch (err) {
    console.log(resource);
    throw err;
  }
}

const testKmsKey = template.findResources("AWS::KMS::Key", {
  Properties: {"Description": "DescriptionTestKey"}
});

const testKmsKeyLogicalId = logicalIdFromResource(testKmsKey);

template.findResources("AWS::SecretsManager::Secret", {
  Properties: {
    "Name": "test",
    "KmsKeyId": {
      "Fn::GetAtt": [
        `${testKmsKeyLogicalId}`,
        "Arn"
      ]
    },
  }
});

Integration Testing

As mentioned at the beginning, integration tests are concerned with deploying resources from a CDK application to a test environment to test the application from start to finish. These tests can take a little more time than a unit test, but they also ensure that the resources are used as intended.

Integration tests allow you to use different assertions to check resources for properties or settings. For example, there is an awsApiCall that can be executed to check whether certain values are in a resource. Here is an example where we check a secret content:

// Check whether the secret was created with the correct content
const assertion = integTest.assertions.awsApiCall("secrets-manager", "GetSecretValue", {
  SecretId: SECRETARN,
}, ["SecretString"]);

assertion.provider.addToRolePolicy({
  Effect: "Allow",
  Action: ["kms:Decrypt*"],
  Resource: ["*"],
});

assertion.assertAtPath("SecretString.username", ExpectedResult.stringLikeRegexp("test"));
assertion.assertAtPath("SecretString.password", ExpectedResult.stringLikeRegexp(".*?"));

With this assertion we use a nice Feature, instead of checking the whole response object at the end, we use assertAtPath to get back only parts of the secret string to do a regex on it.

During the integration test of a CDK app, two stacks are deployed, one stack containing the resources and another stack containing the testing resources. CloudFormation Outputs can be used to pass arns or other variables between the stacks. This would look as follows in the example:

new cdk.CfnOutput(testStack, "AdminSecretArn", {
  value: adminSecret.secretArn,
  exportName: testStack.stackName+"-AdminSecretArn"
});

const SECRETARN = cdk.Fn.importValue(testStack.stackName+"-AdminSecretArn");

In addition to the assertions for awsApiCalls, there is also the invokeFunction. This triggers an AWS Lambda Function that you can define yourself within the assertionStack. Using a lambda function you can execute far more complex tests which return a result at the end which can be checked using the CDK testing framework.

For example, if you do not know exactly how long the function will take or when an object will end up in an s3 bucket, you can use a waitForAssertions method to define at what interval and for how long a lambda function should be triggered:

// Invoke the assertion Lambda function to validate the received payload in s3
integTest.assertions.invokeFunction({
  functionName: assertionLambda.functionName,
}).expect(ExpectedResult.objectLike({
  Payload: "200"
})).waitForAssertions({
  totalTimeout: Duration.minutes(5),
  interval: Duration.seconds(30),
});

Conclusion

Automated unit and integration testing is critical for validating CDK constructs and ensuring the security and compliance of infrastructure code. By improving CDK testing-from unit tests that validate synthesized CloudFormation templates to integration tests that deploy and verify real AWS resources-we can build robust and secure cloud architectures. However, there are still challenges to overcome as the test constructs are still in alpha. We hope that our insights into testing the CDK have helped you and made it easier for you to get started.

Ready for your cloud transformation?

Let's talk about how we can build secure, compliant, and scalable AWS infrastructures for your business.

Get in touch