Fluent AssertJ-like Library For Testing AWS CDK Stack

Muhammad Hamadto
2 min readJun 5, 2023

--

AWS CDK testing framework

AWS offers a framework to verify that your stack is well-formed and that it matches your expectations. This framework can be found here.

Here is an example of the stack containing a specific role:


template.hasResourceProperties("AWS::IAM::Role", Match.objectEquals(
Collections.singletonMap("AssumeRolePolicyDocument", Map.of(
"Version", "2012-10-17",
"Statement", Collections.singletonList(Map.of(
"Action", "sts:AssumeRole",
"Effect", "Allow",
"Principal", Collections.singletonMap(
"Service", Collections.singletonMap(
"Fn::Join", Arrays.asList(
"",
Arrays.asList("states.", Match.anyValue(), ".amazonaws.com")
)
)
)
))
))
));

AWS CDK testing fluent library

While working on a small open-source project, I was maintaining a CDK stack containing some resources. I noticed that the tests written using the AWS framework were verbose and presented challenges in terms of readability and maintainability. In response, I decided to create a small library to make it easier to read, write and maintain tests.

To illustrate, an equivalent representation to the aforementioned example can be found below: as follows”

CDKStackAssert.assertThat(template)
.containsRoleWithManagedPolicyArn(managedPolicyArn)
.hasAssumeRolePolicyDocument("states.amazonaws.com", null, "Allow", "2012-10-17", "sts:AssumeRole");

How to use it

  • The library is available on Maven Central. To use it, add the following dependency to your project:
<dependency>
<groupId>cloud.pianola</groupId>
<artifactId>cdk-fluent-assertions</artifactId>
<version>1.0.1</version>
<scope>test</scope>
</dependency>
  • Create a class TemplateSupport in your testing packages. This class will be used to load the template and create the CDKStackAssert object.
public abstract class TemplateSupport {

public static final String ENV = "test";
public static final String TEST_CDK_BUCKET = "test-cdk-bucket";
public static final String QUALIFIER = "test";
static Template template;
private static final String STACK_NAME = "example-lambda-function-test-stack";

@TempDir
private static Path TEMP_DIR;

@BeforeAll
static void initAll() throws IOException {
final Path lambdaCodePath = TestLambdaUtils.getTestLambdaCodePath(TEMP_DIR);

final Map<String, String> tags = createTags(ENV, TAG_VALUE_COST_CENTRE);
final App app = new App();
final ExampleLambdaStack stack = StackUtils.createStack(app, STACK_NAME, lambdaCodePath.toString(), QUALIFIER, TEST_CDK_BUCKET, ENV);

tags.entrySet().stream()
.filter(tag -> Objects.nonNull(tag.getValue()))
.forEach(tag -> Tags.of(app).add(tag.getKey(), tag.getValue()));

template = Template.fromStack(stack);
}

@AfterAll
static void cleanup() {
template = null;
}
}
  • Extend the TemplateSupport class in your test classes and use the CDKStackAssert object to verify your stack.
class LambdaTest extends TemplateSupport {

public static final String TEST = "test";

@Test
void should_have_lambda_function() {

CDKStackAssert.assertThat(template)
.containsFunction("example-lambda-function")
.hasHandler("org.springframework.cloud.function.adapter.aws.FunctionInvoker::handleRequest")
.hasCode("test-cdk-bucket", "(.*).zip")
.hasRole("examplelambdafunctionrole(.*)")
.hasDependency("examplelambdafunctionrole(.*)")
.hasDependency("examplelambdafunctionroleDefaultPolicy(.*)")
.hasTag("COST_CENTRE", TAG_VALUE_COST_CENTRE)
.hasTag("ENV", TEST)
.hasEnvironmentVariable("ENV", TEST)
.hasEnvironmentVariable("SPRING_PROFILES_ACTIVE", TEST)
.hasDescription("Lambda example")
.hasMemorySize(512)
.hasRuntime("provided.al2")
.hasTimeout(3);
}
}

GitHub project and maven central

--

--