Testing your application can give you faster feedback cycles and guard you against unwanted changes. Testing is currently supported in Typescript with jest and compatible with any testing framework that supports assertions for all other languages.
We generate all files necessary to start testing when you run cdktf init so that you can start writing tests right away.
The following Typescript example uses Testing.synthScope to test a part of the application. This creates a scope to test a subset of the application and returns a JSON string representing the synthesized HCL-JSON. Then it uses custom matchers to verify the code acts as intended.
The other examples use Testing.synth to test a part of the application. Given the desired scope to test, a JSON string representing the synthesized HCL-JSON is returned. Then the custom assertions under Testing in the cdktf package can be used to verify the code acts as intended.
import{Testing}from"cdktf";import{Image,Container}from"../.gen/providers/docker";importMyApplicationsAbstractionfrom"../app";// Could be a class extending from Constructdescribe("Unit testing using assertions",()=>{it("should contain a container",()=>{expect(Testing.synthScope((scope)=>{newMyApplicationsAbstraction(scope,"my-app",{});})).toHaveResource(Container);});it("should use an ubuntu image",()=>{expect(Testing.synthScope((scope)=>{newMyApplicationsAbstraction(scope,"my-app",{});})).toHaveResourceWithProperties(Image,{ name:"ubuntu:latest"});});});
import{Testing}from"cdktf";import{Image,Container}from"../.gen/providers/docker";importMyApplicationsAbstractionfrom"../app";// Could be a class extending from Constructdescribe("Unit testing using assertions",()=>{it("should contain a container",()=>{expect(Testing.synthScope((scope)=>{newMyApplicationsAbstraction(scope,"my-app",{});})).toHaveResource(Container);});it("should use an ubuntu image",()=>{expect(Testing.synthScope((scope)=>{newMyApplicationsAbstraction(scope,"my-app",{});})).toHaveResourceWithProperties(Image,{ name:"ubuntu:latest"});});});
import pytest
from cdktf import Testing
from imports.docker import Image, Container
from app import MyApplicationsAbstraction # Could be a class extending from ConstructclassTestApplication:
stack = TerraformStack(Testing.app(),"stack")
app_abstraction = MyApplicationsAbstraction(stack,"app-abstraction")
synthesized = Testing.synth(stack)deftest_should_contain_container(self):assert Testing.to_have_resource(self.synthesized, Container.TF_RESOURCE_TYPE)deftest_should_use_an_ubuntu_image(self):assert Testing.to_have_resource_with_properties(self.synthesized, Image.TF_RESOURCE_TYPE,{"name":"ubuntu:latest",})
import pytest
from cdktf import Testing
from imports.docker import Image, Container
from app import MyApplicationsAbstraction # Could be a class extending from ConstructclassTestApplication: stack = TerraformStack(Testing.app(),"stack") app_abstraction = MyApplicationsAbstraction(stack,"app-abstraction") synthesized = Testing.synth(stack)deftest_should_contain_container(self):assert Testing.to_have_resource(self.synthesized, Container.TF_RESOURCE_TYPE)deftest_should_use_an_ubuntu_image(self):assert Testing.to_have_resource_with_properties(self.synthesized, Image.TF_RESOURCE_TYPE,{"name":"ubuntu:latest",})
importorg.junit.jupiter.api.Test;importstaticorg.junit.jupiter.api.Assertions.assertTrue;importcom.hashicorp.cdktf.Testing;importimports.docker.Container;importimports.docker.Image;importcom.hashicorp.cdktf.MyApplicationsAbstraction;// Could be a class extending from ConstructpublicclassTestApplication{privatefinalTerraformStack stack =newTerraformStack(Testing.app(),"stack");privatefinalMyApplicationsAbstraction appAbstraction =newMyApplicationsAbstraction(stack,"resource");privatefinalString synthesized =Testing.synth(stack);@TestvoidshouldContainContainer(){assertTrue(Testing.toHaveResource(synthesized,Container.TF_RESOURCE_TYPE));}@TestvoidshouldUseUbuntuImage(){assertTrue(Testing.toHaveResourceWithProperties(synthesized,Image.TF_RESOURCE_TYPE,newHashMap<String,Object>(){{put("name","ubuntu:latest");}}));}}
importorg.junit.jupiter.api.Test;importstaticorg.junit.jupiter.api.Assertions.assertTrue;importcom.hashicorp.cdktf.Testing;importimports.docker.Container;importimports.docker.Image;importcom.hashicorp.cdktf.MyApplicationsAbstraction;// Could be a class extending from ConstructpublicclassTestApplication{privatefinalTerraformStack stack =newTerraformStack(Testing.app(),"stack");privatefinalMyApplicationsAbstraction appAbstraction =newMyApplicationsAbstraction(stack,"resource");privatefinalString synthesized =Testing.synth(stack);@TestvoidshouldContainContainer(){assertTrue(Testing.toHaveResource(synthesized,Container.TF_RESOURCE_TYPE));}@TestvoidshouldUseUbuntuImage(){assertTrue(Testing.toHaveResourceWithProperties(synthesized,Image.TF_RESOURCE_TYPE,newHashMap<String,Object>(){{put("name","ubuntu:latest");}}));}}
usingXunit;usingHashiCorp.Cdktf;// MyApplicationsAbstraction - Could be a class extending from ConstructusingMyCompany.MyApp;usingSystem;usingSystem.Collections.Generic;usingdocker;namespaceMyCompany.MyApp{publicclassTestApplication{privatestaticTerraformStack stack =newTerraformStack(Testing.app(),"stack");privatestaticMyApplicationsAbstraction appAbstraction =newMyApplicationsAbstraction(stack,"resource");privatestaticstring synthesized = Testing.synth(stack);[Fact]publicvoidShouldContainContainer(){
Assert.True(Testing.ToHaveResource(synthesized, Container.TfResourceType));}[Fact]publicvoidshouldUseUbuntuImage(){
Assert.True(Testing.ToHaveResourceWithProperties(synthesized, Image.TfResourceType,newDictionary<String, Object>(){{"name","ubuntu:latest"}}));}}}
usingXunit;usingHashiCorp.Cdktf;// MyApplicationsAbstraction - Could be a class extending from ConstructusingMyCompany.MyApp;usingSystem;usingSystem.Collections.Generic;usingdocker;namespaceMyCompany.MyApp{publicclassTestApplication{privatestaticTerraformStack stack =newTerraformStack(Testing.app(),"stack");privatestaticMyApplicationsAbstraction appAbstraction =newMyApplicationsAbstraction(stack,"resource");privatestaticstring synthesized = Testing.synth(stack);[Fact]publicvoidShouldContainContainer(){ Assert.True(Testing.ToHaveResource(synthesized, Container.TfResourceType));}[Fact]publicvoidshouldUseUbuntuImage(){ Assert.True(Testing.ToHaveResourceWithProperties(synthesized, Image.TfResourceType,newDictionary<String, Object>(){{"name","ubuntu:latest"}}));}}}
Snapshot tests are useful when you want to make sure your infrastructure does not change unexpectedly. Snapshot Testing is only supported in Typescript with Jest. Refer to the Jest docs for details.
import{Testing}from"cdktf";import{Image,Container}from"../.gen/providers/docker";importMyApplicationsAbstractionfrom"../app";// Could be a class extending from Constructdescribe("Unit testing using snapshots",()=>{it("Tests a custom abstraction",()=>{expect(Testing.synthScope((stack)=>{const app =newMyApplicationsAbstraction(scope,"my-app",{});
app.addEndpoint("127.0.0.1");// This could be a method your class exposes})).toMatchInlineSnapshot();// There is also .toMatchSnapshot() to write the snapshot to a file});});
import{Testing}from"cdktf";import{Image,Container}from"../.gen/providers/docker";importMyApplicationsAbstractionfrom"../app";// Could be a class extending from Constructdescribe("Unit testing using snapshots",()=>{it("Tests a custom abstraction",()=>{expect(Testing.synthScope((stack)=>{const app =newMyApplicationsAbstraction(scope,"my-app",{}); app.addEndpoint("127.0.0.1");// This could be a method your class exposes})).toMatchInlineSnapshot();// There is also .toMatchSnapshot() to write the snapshot to a file});});
You can produce invalid Terraform configuration if you are using escape hatches in your CDK for Terraform application. You may use an escape hatch when setting up a remote backend or when overriding resource attributes
To test this, you can assert that terraform validate or terraform plan run successfully on all or part of your application before running cdktf plan or cdktf deploy.
Currently only Typescript is capable of testing for successful plans, while all languages are capable of testing for validity of the Terraform produced.
import{Testing}from"cdktf";describe("Checking validity",()=>{it("check if the produced terraform configuration is valid",()=>{const app =Testing.app();const stack =newTerraformStack(app,"test");const myAbstraction =newMyApplicationsAbstraction(stack,"my-app",{});
myAbstraction.addEndpoint("127.0.0.1");// This could be a method your class exposes// We need to do a full synth to validate the terraform configurationexpect(Testing.fullSynth(stack)).toBeValidTerraform();});it("check if this can be planned",()=>{const app =Testing.app();const stack =newTerraformStack(app,"test");const myAbstraction =newMyApplicationsAbstraction(stack,"my-app",{});
myAbstraction.addEndpoint("127.0.0.1");// This could be a method your class exposes// We need to do a full synth to plan the terraform configurationexpect(Testing.fullSynth(stack)).toPlanSuccessfully();});});
import{Testing}from"cdktf";describe("Checking validity",()=>{it("check if the produced terraform configuration is valid",()=>{const app =Testing.app();const stack =newTerraformStack(app,"test");const myAbstraction =newMyApplicationsAbstraction(stack,"my-app",{}); myAbstraction.addEndpoint("127.0.0.1");// This could be a method your class exposes// We need to do a full synth to validate the terraform configurationexpect(Testing.fullSynth(stack)).toBeValidTerraform();});it("check if this can be planned",()=>{const app =Testing.app();const stack =newTerraformStack(app,"test");const myAbstraction =newMyApplicationsAbstraction(stack,"my-app",{}); myAbstraction.addEndpoint("127.0.0.1");// This could be a method your class exposes// We need to do a full synth to plan the terraform configurationexpect(Testing.fullSynth(stack)).toPlanSuccessfully();});});
import pytest
from cdktf import Testing
from app import MyApplicationsAbstraction # Could be a class extending from ConstructclassTestApplication:
stack = TerraformStack(Testing.app(),"stack")
app_abstraction = MyApplicationsAbstraction(stack,"app-abstraction")deftest_check_validity(self):# We need to do a full synth to validate the terraform configurationassert Testing.to_be_valid_terraform(Testing.full_synth(stack))
import pytest
from cdktf import Testing
from app import MyApplicationsAbstraction # Could be a class extending from ConstructclassTestApplication: stack = TerraformStack(Testing.app(),"stack") app_abstraction = MyApplicationsAbstraction(stack,"app-abstraction")deftest_check_validity(self):# We need to do a full synth to validate the terraform configurationassert Testing.to_be_valid_terraform(Testing.full_synth(stack))
importorg.junit.jupiter.api.Test;importstaticorg.junit.jupiter.api.Assertions.assertTrue;importcom.hashicorp.cdktf.MyApplicationsAbstraction;// Could be a class extending from ConstructpublicclassTestApplication{privatefinalTerraformStack stack =newTerraformStack(Testing.app(),"stack");privatefinalMyApplicationsAbstraction appAbstraction =newMyApplicationsAbstraction(stack,"resource");@TestvoidcheckValidity(){// We need to do a full synth to validate the terraform configurationassertTrue(Testing.toBeValidTerraform(Testing.fullSynth(stack)));}}
importorg.junit.jupiter.api.Test;importstaticorg.junit.jupiter.api.Assertions.assertTrue;importcom.hashicorp.cdktf.MyApplicationsAbstraction;// Could be a class extending from ConstructpublicclassTestApplication{privatefinalTerraformStack stack =newTerraformStack(Testing.app(),"stack");privatefinalMyApplicationsAbstraction appAbstraction =newMyApplicationsAbstraction(stack,"resource");@TestvoidcheckValidity(){// We need to do a full synth to validate the terraform configurationassertTrue(Testing.toBeValidTerraform(Testing.fullSynth(stack)));}}
usingXunit;usingHashiCorp.Cdktf;// MyApplicationsAbstraction - Could be a class extending from ConstructusingMyCompany.MyApp;usingHashiCorp.Cdktf;usingSystem;namespaceMyCompany.MyApp{publicclassTestApplication{privatestaticTerraformStack stack =newTerraformStack(Testing.app(),"stack");privatestaticMyApplicationsAbstraction appAbstraction =newMyApplicationsAbstraction(stack,"construct");[Fact]publicvoidCheckValidity(){// We need to do a full synth to validate the terraform configuration
Assert.True(Testing.ToBeValidTerraform(Testing.FullSynth(stack)));}}}
usingXunit;usingHashiCorp.Cdktf;// MyApplicationsAbstraction - Could be a class extending from ConstructusingMyCompany.MyApp;usingHashiCorp.Cdktf;usingSystem;namespaceMyCompany.MyApp{publicclassTestApplication{privatestaticTerraformStack stack =newTerraformStack(Testing.app(),"stack");privatestaticMyApplicationsAbstraction appAbstraction =newMyApplicationsAbstraction(stack,"construct");[Fact]publicvoidCheckValidity(){// We need to do a full synth to validate the terraform configuration Assert.True(Testing.ToBeValidTerraform(Testing.FullSynth(stack)));}}}
package main
import("testing""github.com/hashicorp/terraform-cdk-go/cdktf""github.com/aws/jsii-runtime-go")var stack = cdktf.NewTerraformStack(cdktf.Testing_App(nil),"stack")funcTestCheckValidity(t *testing.T){// We need to do a full synth to validate the terraform configuration
assertion := cdktf.Testing_ToBeValidTerraform(cdktf.Testing_FullSynth(stack))if!*assertion {
t.Error(assertion.Message())}}
package main
import("testing""github.com/hashicorp/terraform-cdk-go/cdktf""github.com/aws/jsii-runtime-go")var stack = cdktf.NewTerraformStack(cdktf.Testing_App(nil),"stack")funcTestCheckValidity(t *testing.T){// We need to do a full synth to validate the terraform configuration assertion := cdktf.Testing_ToBeValidTerraform(cdktf.Testing_FullSynth(stack))if!*assertion { t.Error(assertion.Message())}}
CDK for Terraform does not currently offer many helpers for integration testing, but you can create them for your use cases. Here is a recent example: CDK Day 2021.