> ## Documentation Index
> Fetch the complete documentation index at: https://cdktn.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Unit Tests - CDK Terrain

> Write assertions and snapshot tests for your CDK Terrain application.

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 `cdktn init` so that you can start writing tests right away.

## Add Testing to Your Application

If you would like to add testing to an existing project, refer the following resources according to your chosen language:

* **TypeScript:** Follow TypeScript's [Getting Started guide](https://jestjs.io/getting-started) for testing with Jest. Then, add these lines in a [setup file](https://jestjs.io/configuration#setupfiles-array):

  ```js theme={null}
  const cdktn = require("cdktn");
  cdktn.Testing.setupJest();
  ```

* **Python:** Follow the [Get Started guide](https://docs.pytest.org/en/7.1.x/getting-started.html) for pytest. The assertions for CDKTN are available in the `cdktn` package under `Testing`.

* **Java:** Follow the [Using JUnit documentation](https://docs.gradle.org/current/userguide/java_testing.html). The assertions for CDKTN specific are available in the `cdktn` package under `Testing`.

* **C#:** Follow the [Getting Started with xUnit.net guide](https://xunit.net/getting-started/netcore/cmdline). The assertions for CDKTN are available in the `cdktn` package under `Testing`.

* **Go:** Follow the [Add a Test guide](https://go.dev/doc/tutorial/add-a-test) in the Go documentation. The assertions for CDKTN specific are available in the `cdktn` package under `Testing`.

### Write Assertions

The following Typescript example uses `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 cdktn package can be used to verify the code acts as intended. `Testing.synth` can test the `Stack` that extends the `TerraformStack`.

The other examples use `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. `Testing.synthScope` can test the `Constructs` that extends the `IConstruct`.

<Note>CDK Terrain v0.20 introduces support for a `Testing.synthHcl` function. However, that is not compatible with other assertions, like `toHaveResourceWithProperties` etc. and should not be used.</Note>

Examples in

* `toHaveResource`: Checks if a certain resource exists
* `toHaveResourceWithProperties`: Checks if a certain resource exists with all properties passed
* `toHaveDataSource`: Checks if a certain data source exists
* `toHaveDataSourceWithProperties`: Checks if a certain data source exists with all properties passed
* `toHaveProvider`: Checks if a certain provider exists
* `toHaveProviderWithProperties`: Checks if a certain provider exists with all properties passed

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Testing } from "cdktn";
  import { Image } from "../.gen/providers/docker/image";
  import { Container } from "../.gen/providers/docker/container";
  import MyApplicationsAbstraction from "../app"; // Could be a class extending from Construct

  describe("Unit testing using assertions", () => {
    it("should contain a container", () => {
      const app = Testing.app();
      const stack = new MyApplicationsAbstraction(app, "my-app", {});
      const synthesized = Testing.synth(stack);

      expect(synthesized).toHaveResource(Container);
    });

    it("should use an ubuntu image", () => {
      const app = Testing.app();
      const stack = new MyApplicationsAbstraction(app, "my-app", {});
      const synthesized = Testing.synth(stack);

      expect(synthesized).toHaveResourceWithProperties(Image, {
        name: "ubuntu:latest",
      });
    });
  });
  ```

  ```python Python theme={null}
  import pytest
  from cdktn import Testing
  from imports.docker.image import Image
  from imports.docker.container import Container
  from app import MyApplicationsAbstraction # Could be a class extending from Construct

  class TestApplication:

    stack = TerraformStack(Testing.app(), "stack")
    app_abstraction = MyApplicationsAbstraction(stack, "app-abstraction")
    synthesized = Testing.synth(stack)

    def test_should_contain_container(self):
      assert Testing.to_have_resource(self.synthesized, Container.TF_RESOURCE_TYPE)

    def test_should_use_an_ubuntu_image(self):
      assert Testing.to_have_resource_with_properties(self.synthesized, Image.TF_RESOURCE_TYPE, {
              "name": "ubuntu:latest",
      })
  ```

  ```java Java theme={null}
  import org.junit.jupiter.api.Test;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  import io.cdktn.cdktn.Testing;
  import imports.docker.Container;
  import imports.docker.Image;
  import io.cdktn.cdktn.MyApplicationsAbstraction; // Could be a class extending from Construct

  public class TestApplication {

    private final TerraformStack stack = new TerraformStack(Testing.app(), "stack");
    private final MyApplicationsAbstraction appAbstraction = new MyApplicationsAbstraction(stack, "resource");
    private final String synthesized = Testing.synth(stack);

    @Test
    void shouldContainContainer() {
      assertTrue(Testing.toHaveResource(synthesized, Container.TF_RESOURCE_TYPE) );
    }

    @Test
    void shouldUseUbuntuImage() {
      assertTrue(Testing.toHaveResourceWithProperties(synthesized, Image.TF_RESOURCE_TYPE, new HashMap<String, Object>() {
                  {
                      put("name", "ubuntu:latest");
                  }
      }) );
    }
  }
  ```

  ```csharp C# theme={null}
  using Xunit;
  using Io.Cdktn; // MyApplicationsAbstraction - Could be a class extending from Construct
  using MyCompany.MyApp;
  using System;
  using System.Collections.Generic;
  using docker.Image;
  using docker.Container;

  namespace MyCompany.MyApp{
    public class TestApplication{

      private static TerraformStack stack = new TerraformStack(Testing.app(), "stack");
      private static MyApplicationsAbstraction appAbstraction = new MyApplicationsAbstraction(stack, "resource");
      private static string synthesized = Testing.synth(stack);

      [Fact]
      public void ShouldContainContainer(){
        Assert.True(Testing.ToHaveResource(synthesized, Container.TfResourceType) );
      }

      [Fact]
      public void shouldUseUbuntuImage(){
        Assert.True(Testing.ToHaveResourceWithProperties(synthesized, Image.TfResourceType, new Dictionary<String, Object>() {
                {"name", "ubuntu:latest"}
        }) );
      }
    }
  }
  ```

  ```go Go theme={null}
  package main

  import (
  	"testing"
  	"cdk.tf/go/stack/generated/kreuzwerker/docker/image"
  	"cdk.tf/go/stack/generated/kreuzwerker/docker/container"
  	"github.com/open-constructs/cdk-terrain-go/cdktn"
  	"github.com/aws/jsii-runtime-go"
  )

  var stack = NewMyApplicationsAbstraction(cdktn.Testing_App(nil), "stack")
  var synth = cdktn.Testing_Synth(stack)

  func TestShouldContainContainer(t *testing.T){
  	assertion := cdktn.Testing_ToHaveResource(synth, container.Container_TfResourceType())

  	if !*assertion  {
  		t.Error("Assertion Failed")
  	}
  }

  func TestShouldUseUbuntuImage(t *testing.T){
  	properties := map[string]interface{}{
  		"name": "ubuntu:latest",
  	}
  	assertion := cdktn.Testing_ToHaveResourceWithProperties(synth, image.Image_TfResourceType(), &properties)

  	if !*assertion  {
  		t.Error("Assertion Failed")
  	}
  }
  ```
</CodeGroup>

### Snapshot Testing

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](https://jestjs.io/snapshot-testing) for details.

```typescript theme={null}
import { Testing } from "cdktn";
import { Image } from "../.gen/providers/docker/image";
import { Container } from "../.gen/providers/docker/container";
import MyApplicationsAbstraction from "../app"; // Could be a class extending from Construct

describe("Unit testing using snapshots", () => {
  it("Tests a custom abstraction", () => {
    expect(
      Testing.synthScope((stack) => {
        const app = new MyApplicationsAbstraction(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
  });
});
```

### Integration with Terraform

You can produce invalid Terraform configuration if you are using escape hatches in your CDK Terrain application. You may use an escape hatch when setting up a [remote backend](/concepts/remote-backends) or when [overriding resource attributes](/concepts/resources#escape-hatch)

To test this, you can assert that [`terraform validate`](/terraform/cli/commands/validate) or [`terraform plan`](/terraform/cli/commands/plan) run successfully on all or part of your application before running `cdktn plan` or `cdktn deploy`.

Currently only Typescript is capable of testing for successful plans, while all languages are capable of testing for validity of the Terraform produced.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Testing } from "cdktn";

  describe("Checking validity", () => {
    it("check if the produced terraform configuration is valid", () => {
      const app = Testing.app();
      const stack = new TerraformStack(app, "test");

      const myAbstraction = new MyApplicationsAbstraction(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 configuration
      expect(Testing.fullSynth(stack)).toBeValidTerraform();
    });

    it("check if this can be planned", () => {
      const app = Testing.app();
      const stack = new TerraformStack(app, "test");

      const myAbstraction = new MyApplicationsAbstraction(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 configuration
      expect(Testing.fullSynth(stack)).toPlanSuccessfully();
    });
  });
  ```

  ```python Python theme={null}
  import pytest
  from cdktn import Testing
  from app import MyApplicationsAbstraction # Could be a class extending from Construct

  class TestApplication:

    stack = TerraformStack(Testing.app(), "stack")
    app_abstraction = MyApplicationsAbstraction(stack, "app-abstraction")

    def test_check_validity(self):
      # We need to do a full synth to validate the terraform configuration
      assert Testing.to_be_valid_terraform(Testing.full_synth(stack))
  ```

  ```java Java theme={null}
  import org.junit.jupiter.api.Test;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  import io.cdktn.cdktn.MyApplicationsAbstraction; // Could be a class extending from Construct

  public class TestApplication {

    private final TerraformStack stack = new TerraformStack(Testing.app(), "stack");
    private final MyApplicationsAbstraction appAbstraction = new MyApplicationsAbstraction(stack, "resource");

    @Test
    void checkValidity() {
      // We need to do a full synth to validate the terraform configuration
      assertTrue(Testing.toBeValidTerraform(Testing.fullSynth(stack)) );
    }
  }
  ```

  ```csharp C# theme={null}
  using Xunit;
  using Io.Cdktn; // MyApplicationsAbstraction - Could be a class extending from Construct
  using MyCompany.MyApp;
  using System;

  namespace MyCompany.MyApp{
    public class TestApplication{

      private static TerraformStack stack = new TerraformStack(Testing.app(), "stack");
      private static MyApplicationsAbstraction appAbstraction = new MyApplicationsAbstraction(stack, "construct");

      [Fact]
      public void CheckValidity(){
        // We need to do a full synth to validate the terraform configuration
        Assert.True(Testing.ToBeValidTerraform(Testing.FullSynth(stack)) );
      }
    }
  }
  ```

  ```go Go theme={null}
  package main

  import (
  	"testing"
  	"github.com/open-constructs/cdk-terrain-go/cdktn"
  	"github.com/aws/jsii-runtime-go"
  )

  var stack = cdktn.NewTerraformStack(cdktn.Testing_App(nil), "stack")

  func TestCheckValidity(t *testing.T){
    // We need to do a full synth to validate the terraform configuration
  	assertion := cdktn.Testing_ToBeValidTerraform(cdktn.Testing_FullSynth(stack))

  	if !*assertion  {
  		t.Error("Assertion Failed")
  	}
  }
  ```
</CodeGroup>

## Integration Testing

CDK Terrain 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](https://github.com/ansgarm/talk-cdkday-2021/tree/master/test).
