Testing AWS resources with Terratest
This guide will walk you through adding Terratest to a Nitric project.
How Terratest works
Terratest is designed to automate the entire process of testing your Terraform code with the following steps:
- Initialize: Terratest will automatically run
terraform init
to initialize the Terraform working directory. - Apply: It will then run
terraform apply
to deploy the infrastructure as defined in your Terraform code. - Assert: The test script will then run assertions to check that the infrastructure was created as expected.
- Teardown: Finally, it will run
terraform destroy
to tear down the infrastructure after the test completes.
What we'll be doing
- Create and set up your application.
- Deploying to AWS with a Terraform provider.
- Add and execute Terratest.
Create and set up your application.
Our sample project creates a real-time communication service using WebSockets and a key-value store for connections.
We intend to deploy to AWS and will use Terratest to ensure that the following:
- WebSocket API: Confirm that the API Gateway WebSocket API is correctly configured for real-time communication.
- DynamoDB Table: Verify that the key-value store for connections is created and operational.
- IAM Roles: Ensure that permissions for interacting with AWS services are correctly set up.
Let's begin by setting up the WebSocket application that we'll be testing.
You can find a more detailed set of instructions in this guide - Building a chat app in Go with WebSockets and Nitric . Once you've completed this guide, skip to the next step
nitric new websocket-app go-starter
Then you can delete all files/folders in the services/
folder and add a file named main.go
to your 'services/websockets' folder with the following code:
package mainimport ("context""fmt""github.com/nitrictech/go-sdk/nitric""github.com/nitrictech/go-sdk/nitric/keyvalue""github.com/nitrictech/go-sdk/nitric/websockets")func main() {// Create a WebSocket endpoint named "public".ws := nitric.NewWebsocket("public")// Initialize a KV store named "connections" with Get, Set, and Delete permissions.connections := nitric.NewKv("connections").Allow(keyvalue.KvStoreGet, keyvalue.KvStoreSet, keyvalue.KvStoreDelete)// Handle new WebSocket connections by storing the connection ID in the KV store.ws.On(websockets.EventType_Connect, func(ctx *websockets.Ctx) {err := connections.Set(context.Background(), ctx.Request.ConnectionID(), map[string]interface{}{"connectionId": ctx.Request.ConnectionID(),})if err != nil {fmt.Println("Error storing connection ID in KV store:", err)return}})ws.On(websockets.EventType_Disconnect, func(ctx *websockets.Ctx) {err := connections.Delete(context.Background(), ctx.Request.ConnectionID())if err != nil {fmt.Println("Error deleting connection ID in KV store:", err)return}})ws.On(websockets.EventType_Message, func(ctx *websockets.Ctx) {connectionStream, err := connections.Keys(context.Background())if err != nil {fmt.Println("Error retrieving connection keys from KV store:", err)return}senderId := ctx.Request.ConnectionID()for {connectionId, err := connectionStream.Recv()if err != nil {break}if connectionId == senderId {continue}message := fmt.Sprintf("%s: %s", senderId, ctx.Request.Message())err = ws.Send(context.Background(), connectionId, []byte(message))if err != nil {fmt.Println("Error sending message to connection ID", connectionId, ":", err)return}}})nitric.Run()}
Deploying to AWS with a Terraform provider
To deploy your application with Terraform you'll need to use Nitric's Terraform providers. You can learn more about using Nitric with Terraform here.
nitric stack new dev aws-tf
Update this newly created stack file to include your target region:
# The nitric provider to useprovider: nitric/awstf@1.11.6# The target aws region to deploy toregion: us-east-2
The Nitric Terraform providers are currently in preview, to enable them you'll need to enable beta-providers in your Nitric project. You can do this by adding the following to your project's nitric.yaml
file:
preview:- beta-providers
Once you've created your stack file, you can generate the Terraform code by running the following command:
nitric up
This will generate Terraform code which can deploy your application. The output will be in a folder named cdktf.out
by default.
Add and execute Terratest
Add the necessary dependencies for Terratest:
go get github.com/gruntwork-io/terratest/modules/terraformgo get github.com/stretchr/testify/assertgo get github.com/aws/aws-sdk-go/awsgo get github.com/aws/aws-sdk-go/aws/sessiongo get github.com/aws/aws-sdk-go/service/apigatewayv2go get github.com/aws/aws-sdk-go/service/dynamodbgo get github.com/aws/aws-sdk-go/service/iamgo get google.golang.org/genproto@latest
Create the Test File
Create a new file named terraform_resources_test.go
in your project’s test directory:
mkdir -p testtouch test/terraform_resources_test.go
Add the following code to terraform_resources_test.go
:
package testimport ("testing""github.com/aws/aws-sdk-go/aws""github.com/aws/aws-sdk-go/aws/session""github.com/aws/aws-sdk-go/service/apigatewayv2""github.com/aws/aws-sdk-go/service/dynamodb""github.com/aws/aws-sdk-go/service/iam""github.com/gruntwork-io/terratest/modules/terraform""github.com/stretchr/testify/assert")func TestTerraformResources(t *testing.T) {// Set Terraform options, specifying the directory with your Terraform configurationterraformOptions := &terraform.Options{TerraformDir: "../cdktf.out/stacks/websocket-app-dev",}// Ensure resources are destroyed after test completiondefer terraform.Destroy(t, terraformOptions)// Initialize and apply the Terraform configurationterraform.InitAndApply(t, terraformOptions)// Initialize AWS session for interacting with AWS servicessess := session.Must(session.NewSession(&aws.Config{Region: aws.String("us-east-2")}))// Test DynamoDB table creation (key-value store)dynamoClient := dynamodb.New(sess)tableName := "connections" // Name of the DynamoDB table to check_, err := dynamoClient.DescribeTable(&dynamodb.DescribeTableInput{TableName: aws.String(tableName),})assert.NoError(t, err, "Expected DynamoDB table 'connections' to be created")// Test IAM role creationiamClient := iam.New(sess)roleName := "websocket-app_websockets-main" // Name of the IAM role to check_, err = iamClient.GetRole(&iam.GetRoleInput{RoleName: aws.String(roleName),})assert.NoError(t, err, "Expected IAM role 'websocket-app_websockets-main' to be created")// Test API gateway webSocket creationapiClient := apigatewayv2.New(sess)apiName := "public" // Name of the API Gateway WebSocket to checkresult, err := apiClient.GetApis(&apigatewayv2.GetApisInput{})assert.NoError(t, err)found := falsefor _, api := range result.Items {if *api.Name == apiName {found = truebreak}}assert.True(t, found, "Expected API Gateway WebSocket 'public' to be created")}
Run the tests
To run the tests, navigate to your project’s root directory and execute the Go test command:
go test -v ./test
This will:
- Deploy the infrastructure using Terraform.
- Validate the creation of the DynamoDB table, IAM role, and API Gateway WebSocket.
- Clean up the infrastructure after testing.
The output should confirm the successful creation and validation of each resource.