A logo showing the text blog.marcnuri.com
Español
Home»Go»Getting started with Testcontainers for Go

Recent Posts

  • Fabric8 Kubernetes Client 7.2 is now available!
  • Connecting to an MCP Server from JavaScript using AI SDK
  • Connecting to an MCP Server from JavaScript using LangChain.js
  • The Future of Developer Tools: Adapting to Machine-Based Developers
  • Connecting to a Model Context Protocol (MCP) Server from Java using LangChain4j

Categories

  • Artificial Intelligence
  • Front-end
  • Go
  • Industry and business
  • Java
  • JavaScript
  • Legacy
  • Operations
  • Personal
  • Pet projects
  • Tools

Archives

  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • August 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • March 2023
  • February 2023
  • January 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • July 2022
  • June 2022
  • May 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • February 2020
  • January 2020
  • December 2019
  • October 2019
  • September 2019
  • July 2019
  • March 2019
  • November 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
  • February 2018
  • December 2017
  • July 2017
  • January 2017
  • December 2015
  • November 2015
  • December 2014
  • March 2014
  • February 2011
  • November 2008
  • June 2008
  • May 2008
  • April 2008
  • January 2008
  • November 2007
  • September 2007
  • August 2007
  • July 2007
  • June 2007
  • May 2007
  • April 2007
  • March 2007

Getting started with Testcontainers for Go

2023-11-03 in Go tagged Go / Testcontainers / Testing / Test-Driven Development (TDD) / HTTP by Marc Nuri | Last updated: 2023-11-03
Versión en Español

Introduction

Testcontainers is a powerful open source library designed for integration testing in your Go applications. This library provides an easy way to create and manage ephemeral Docker containers, allowing you to simulate various dependencies like databases, message brokers, or other services required for your application's functionality during the testing phase. By using Testcontainers, you can test your application in an environment that closely resembles your production setup, reducing the need for mocks or expensive testing environments.

In this post, I will walk you through the process of getting started with Testcontainers, focusing on how to incorporate it into your Go project. We will begin by creating a new Go project and adding the Testcontainers library as a dependency. Then, I will demonstrate how to create and interact with a Docker container running a simple HTTP server as part of your tests.

You can find the source code for this tutorial on GitHub.

Creating the project

Let's start by setting up a new project to explore Testcontainers in action.

Initializing a new Go module

We'll use Go Modules to manage our dependencies. To begin, create a new directory and initialize a new Go module:

bash
mkdir testcontainers-go-getting-started
cd testcontainers-go-getting-started
go mod init github.com/marcnuri-demo/testcontainers-go-getting-started

If the process goes smoothly, you should have a new go.mod file with the following content:

go.mod
module github.com/marcnuri-demo/testcontainers-go-getting-started

go 1.21.2

Creating the main package

Next, we'll create a new file main_test.go, which will serve as the foundation for our test implementation:

main_test.go
package main

import "testing"

func TestExample(t *testing.T) {
}

Adding the Testcontainers dependency

Now that we have the test structure in place, we can add the Testcontainers library as a dependency. To do so, use the go get command:

bash
go get github.com/testcontainers/testcontainers-go

This command will include the Testcontainers dependency in your go.mod file and bootstrap a go.sum file containing the dependency checksums.

Implementing the test

With the project structure and Testcontainers dependency in place, we can start working on our test.

We'll start by seeing how to spin up a new container running a simple HTTP server.

Spinning up a new container

Let's modify the TestExample function to include a Testconainers request for creating a new container:

main_test.go
func TestExample(t *testing.T) {
	containerCtx := context.Background()
	chuckNorris, _ := testcontainers.GenericContainer(containerCtx, testcontainers.GenericContainerRequest{
		Started: true,
		ContainerRequest: testcontainers.ContainerRequest{
			Image:        "marcnuri/chuck-norris:latest",
			ExposedPorts: []string{"8080/tcp"},
			WaitingFor:   wait.ForLog("Listening on: http://0.0.0.0:8080"),
		},
	})
	defer func() {
		err := chuckNorris.Terminate(containerCtx)
		if err != nil {
			panic(err)
		}
	}()
}

Here's a breakdown of what's happening in the code above:

  • We initiate a Go context called containerCtx that will be used by Testcontainers to manage the information about the container lifecycle.
  • Using the GenericContainer function, we create a new container. This function requires a Go context as its first argument. The second parameter is a GenericContainerRequest struct with the following fields:
    • Started: A boolean that indicates whether the container should be started as soon as it's created. Since we want the container to start automatically, we set it to true.
    • ContainerRequest: A ContainerRequest struct that contains the information about the container we want to spin up. In this case, we want to create a new container using the marcnuri/chuck-norris:latest image which exposes an HTTP API on port 8080.

Let's take a more detailed look at the ContainerRequest struct fields:

  • Image: The Docker image to use for the container.
  • ExposedPorts: An array specifying the ports to expose from the container.
  • WaitingFor: A LogStrategy struct that will be used to wait for the container to be ready. In this example, we use the ForLog function to wait for the container to log the message Listening on: http://0.0.0.0:8080 which is the message that the container logs when it's ready to accept requests.

Finally, we defer a function to ensure the container is terminated after the test concludes, regardless of the test's outcome.

You can execute the tests by running the following command:

bash
go test

If all goes well, you should see an output similar to the following:

bash
github.com/testcontainers/testcontainers-go - Connected to docker:
  Resolved Docker Host: unix:///var/run/docker.sock
  Resolved Docker Socket Path: /var/run/docker.sock
  Test SessionID: 459a37cc7f20d8c0f04f777e23bcea0cfe0556ba695e226e643a700ed927b4b1
  Test ProcessID: 52bf9bdd-48a2-4744-8f4e-52cfef44a17d
🐳 Creating container for image marcnuri/chuck-norris:latest
✅ Container created: a7a9ef65cf1a
🐳 Starting container: a7a9ef65cf1a
✅ Container started: a7a9ef65cf1a
🚧 Waiting for container id a7a9ef65cf1a image: marcnuri/chuck-norris:latest. Waiting for: &{timeout:<nil> Log:Listening on: http://0.0.0.0:8080 IsRegexp:false Occurrence:1 PollInterval:100ms}
🐳 Terminating container: a7a9ef65cf1a
🚫 Container terminated: a7a9ef65cf1a

Making requests to the container

Now that we have a running container, let's see how to interact with it. We'll modify the TestExample function to make a request to the container and validate its response:

main_test.go
func TestExample(t *testing.T) {
  // ... Testcontainers initialization code
	endpoint, err := chuckNorris.Endpoint(containerCtx, "http")
	if err != nil {
		t.Error(err)
	}
	response, err := http.Get(endpoint)
	if err != nil {
		t.Error(err)
	}
	if response.StatusCode != http.StatusOK {
		t.Errorf("Expected status code %d, got %d", http.StatusOK, response.StatusCode)
	}
	body, err := io.ReadAll(response.Body)
	if err != nil {
		t.Error(err)
	}
	bodyString := string(body)
	if !strings.Contains(strings.ToLower(bodyString), "chuck") {
		t.Errorf("Expected a Chuck Norris approved response, got \"%s\"", bodyString)
	}
}

The code snippet above does the following:

  • Using the Endpoint function, we retrieve the container's endpoint. This function requires a Go context as its first argument and a protocol as its second argument. In the example, we want to retrieve the HTTP endpoint, so we pass http as the second argument.
  • Using the http.Get function, we make a request to the container's endpoint.
  • We validate that the response status code is 200 OK.
  • We read the response body and validate that it contains the word Chuck.

You should be able to execute the tests once again by running go test. Everything should work as expected, and you should see the test passing.

Conclusion

In this article, we've seen how to get started with Testcontainers for Go. We began by creating a new Go project from scratch and adding the Testcontainers dependency. Then, we implemented a simple test that spins up a new container and makes an HTTP request to it. You should now have a solid foundation for incorporating Testcontainers into your Go projects.

You can find the full source code for this post at GitHub.

Happy testing!

Twitter iconFacebook iconLinkedIn iconPinterest iconEmail icon

Post navigation
Four years at Red HatTesting Go Gin Web Framework REST APIs with httptest
© 2007 - 2025 Marc Nuri