Testing is a crucial part of software development, and Go provides built-in support for testing through the testing
package. In this section, we will explore how to write and run tests in Go, as well as some best practices for testing your code.
To write a test in Go, you need to create a new file with the _test.go
suffix. Inside this file, you can define test functions that start with the word Test
. Each test function should take a pointer to testing.T
as its only argument. Here's an example of a simple test:
// file: adder.go
package adder
func Add(a, b int) int {
return a + b
}
// file: adder_test.go
package adder
import (
"testing"
)
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Expected 5, got %d", result)
}
}
The TestAdd
function tests the Add
function from the adder
package. It checks if the result of adding 2 and 3 is equal to 5. If the test fails, it uses t.Errorf
to report the error.
To run your test, you can use the go test
command. This command will automatically find and run all the tests in your package. You can also run tests in a specific file by providing the filename as an argument:
$ go test -v
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok example.com/adder 0.001s
There is no built-in assertion library in Go, but there is well-known third-party libraries that provide assertion functions. One of the most popular libraries is stretchr/testify
. You can install it using the following command:
go get github.com/stretchr/testify
package adder_test
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAdd(t *testing.T) {
result := Add(2, 3)
assert.Equal(t, 5, result)
}
If you do multiple assertions in a single test, you can use pattern below:
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
assert := assert.New(t)
var a string = "Hello"
var b string = "Hello"
assert.Equal(a, b, "The two words should be the same.")
}
Code coverage is a measure of how much of your code is executed during testing. Go provides built-in support for measuring code coverage using the -cover
flag with the go test
command. Here's how to run tests with coverage:
$ go test -coverprofile <filename> <package_name>
Test containers are a way to run tests in isolated environments using Docker containers. This is particularly useful for testing code that interacts with external services, such as databases or APIs. The testcontainers-go
library provides a simple way to create and manage test containers in Go.
In traditional testing, you might need to setup databases, message queues, or other services before running your tests. This can be cumbersome and time-consuming. Test containers allow you to spin up these services in Docker containers, run your tests, and then tear down the containers afterward.
You can install it using the following command:
go get github.com/testcontainers/testcontainers-go
Assume you need to test a function that connects to Redis. Then you can use the testcontainers-go
library to create a Redis container for your tests. Here's an example:
// file: cache.go
package cache
import (
"github.com/redis/go-redis/v9"
"context"
)
func GetCache(client *redis.Client, key string) (string, error) {
ctx := context.Background()
val, err := client.Get(ctx, key).Result()
if err != nil {
return "", err
}
return val, nil
}
// file cache_test.go
package cache
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/redis/go-redis/v9"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)
func TestCache(t *testing.T) {
ctx := context.Background()
req := testcontainers.ContainerRequest{
Image: "redis:latest",
ExposedPorts: []string{"6379/tcp"},
WaitingFor: wait.ForLog("Ready to accept connections"),
}
redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
})
endpoint, err := redisC.Endpoint(ctx, "")
if err != nil {
t.Error(err)
}
client := redis.NewClient(&redis.Options{
Addr: endpoint,
})
// Set a key in Redis
err = client.Set(ctx, "key", "value", 0).Err()
if err != nil {
t.Error(err)
}
// Test the GetCache function
val, err := GetCache(client, "key")
require.NoError(t, err)
require.Equal(t, "value", val)
// Cleanup
testcontainers.CleanupContainer(t, redisC)
require.NoError(t, err)
}