Package testutils
provides useful test helpers.
CaptureStdout
: Captures stdout output from the provided functionCaptureStderr
: Captures stderr output from the provided functionCaptureStdoutAndStderr
: Captures both stdout and stderr from the provided function
These capture utilities are useful for testing functions that write directly to stdout/stderr. They redirect the standard outputs to a buffer and return the captured content as a string.
Important Note: The capture functions are not thread-safe if used in parallel tests. For concurrent tests, it's better to pass a custom io.Writer to the function under test instead.
WriteTestFile
: Creates a temporary file with specified content and returns its path. The file is automatically cleaned up after the test completes.
MockHTTPServer
: Creates a test HTTP server with the given handler. Returns the server URL and a cleanup function.HTTPRequestCaptor
: Returns a request captor and an HTTP handler that captures and records HTTP requests for later inspection.
The containers
package provides several test containers for integration testing:
SSHTestContainer
: SSH server container for testing SSH connections with file operations (upload, download, list, delete)FTPTestContainer
: FTP server container for testing FTP file operations (upload, download, list, delete)PostgresTestContainer
: PostgreSQL database container with automatic database creationMySQLTestContainer
: MySQL database container with automatic database creationMongoTestContainer
: MongoDB container with support for multiple versions (5, 6, 7)LocalstackTestContainer
: LocalStack container with S3 service for AWS testing, including file operations (upload, download, list, delete)
go get -u github.com/go-pkgz/testutils
// Capture stdout
func TestMyFunction(t *testing.T) {
output := testutils.CaptureStdout(t, func() {
fmt.Println("Hello, World!")
})
assert.Equal(t, "Hello, World!\n", output)
}
// Capture stderr
func TestErrorOutput(t *testing.T) {
errOutput := testutils.CaptureStderr(t, func() {
fmt.Fprintln(os.Stderr, "Error message")
})
assert.Equal(t, "Error message\n", errOutput)
}
// Capture both stdout and stderr
func TestBothOutputs(t *testing.T) {
stdout, stderr := testutils.CaptureStdoutAndStderr(t, func() {
fmt.Println("Standard output")
fmt.Fprintln(os.Stderr, "Error output")
})
assert.Equal(t, "Standard output\n", stdout)
assert.Equal(t, "Error output\n", stderr)
}
// Create a temporary test file
func TestWithTempFile(t *testing.T) {
content := "test file content"
filePath := testutils.WriteTestFile(t, content)
// Use the file in your test
data, err := os.ReadFile(filePath)
require.NoError(t, err)
assert.Equal(t, content, string(data))
// No need to clean up - it happens automatically when the test ends
}
// Create a mock HTTP server
func TestWithMockServer(t *testing.T) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("response"))
})
serverURL, _ := testutils.MockHTTPServer(t, handler)
// Make requests to the server
resp, err := http.Get(serverURL + "/path")
require.NoError(t, err)
defer resp.Body.Close()
assert.Equal(t, http.StatusOK, resp.StatusCode)
}
// Capture and inspect HTTP requests
func TestWithRequestCaptor(t *testing.T) {
// Create a request captor
captor, handler := testutils.HTTPRequestCaptor(t, nil)
// Create a server with the capturing handler
serverURL, _ := testutils.MockHTTPServer(t, handler)
// Make a request
http.Post(serverURL+"/api", "application/json",
strings.NewReader(`{"key":"value"}`))
// Inspect the captured request
req, _ := captor.GetRequest(0)
assert.Equal(t, http.MethodPost, req.Method)
assert.Equal(t, "/api", req.Path)
assert.Equal(t, `{"key":"value"}`, string(req.Body))
}
// PostgreSQL test container
func TestWithPostgres(t *testing.T) {
ctx := context.Background()
pg := containers.NewPostgresTestContainer(ctx, t)
defer pg.Close(ctx)
db, err := sql.Open("postgres", pg.ConnectionString())
require.NoError(t, err)
defer db.Close()
// run your tests with the database
}
// MySQL test container
func TestWithMySQL(t *testing.T) {
ctx := context.Background()
mysql := containers.NewMySQLTestContainer(ctx, t)
defer mysql.Close(ctx)
db, err := sql.Open("mysql", mysql.DSN())
require.NoError(t, err)
defer db.Close()
// run your tests with the database
}
// MongoDB test container
func TestWithMongo(t *testing.T) {
ctx := context.Background()
mongo := containers.NewMongoTestContainer(ctx, t, 7) // version 7
defer mongo.Close(ctx)
coll := mongo.Collection("test_db")
_, err := coll.InsertOne(ctx, bson.M{"test": "value"})
require.NoError(t, err)
}
// SSH test container
func TestWithSSH(t *testing.T) {
ctx := context.Background()
ssh := containers.NewSSHTestContainer(ctx, t)
defer ssh.Close(ctx)
// use ssh.Address() to get host:port
// default user is "test"
sshAddr := ssh.Address()
// Upload a file to the SSH server
localFile := "/path/to/local/file.txt"
remotePath := "/config/file.txt"
err := ssh.SaveFile(ctx, localFile, remotePath)
require.NoError(t, err)
// Download a file from the SSH server
downloadPath := "/path/to/download/location.txt"
err = ssh.GetFile(ctx, remotePath, downloadPath)
require.NoError(t, err)
// List files on the SSH server
files, err := ssh.ListFiles(ctx, "/config")
require.NoError(t, err)
for _, file := range files {
fmt.Println(file.Name(), file.Mode(), file.Size())
}
// Delete a file from the SSH server
err = ssh.DeleteFile(ctx, remotePath)
require.NoError(t, err)
}
// Localstack (S3) test container
func TestWithS3(t *testing.T) {
ctx := context.Background()
ls := containers.NewLocalstackTestContainer(ctx, t)
defer ls.Close(ctx)
s3Client, bucketName := ls.MakeS3Connection(ctx, t)
// put object example (using direct S3 API)
_, err := s3Client.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String("test-key"),
Body: strings.NewReader("test content"),
})
require.NoError(t, err)
// File operations using higher-level container methods
// Upload a file to S3
localFile := "/path/to/local/file.txt"
objectKey := "documents/file.txt"
err = ls.SaveFile(ctx, localFile, bucketName, objectKey)
require.NoError(t, err)
// Download a file from S3
downloadPath := "/path/to/download/location.txt"
err = ls.GetFile(ctx, bucketName, objectKey, downloadPath)
require.NoError(t, err)
// List objects in bucket (optionally with prefix)
objects, err := ls.ListFiles(ctx, bucketName, "documents/")
require.NoError(t, err)
for _, obj := range objects {
fmt.Println(*obj.Key, *obj.Size)
}
// Delete an object from S3
err = ls.DeleteFile(ctx, bucketName, objectKey)
require.NoError(t, err)
}
// FTP test container
func TestWithFTP(t *testing.T) {
ctx := context.Background()
ftpContainer := containers.NewFTPTestContainer(ctx, t)
defer ftpContainer.Close(ctx)
// Connection details
ftpHost := ftpContainer.GetIP() // Container host
ftpPort := ftpContainer.GetPort() // Container port (default: 2121)
ftpUser := ftpContainer.GetUser() // Default: "ftpuser"
ftpPassword := ftpContainer.GetPassword() // Default: "ftppass"
// Upload a file
localFile := "/path/to/local/file.txt"
remotePath := "file.txt"
err := ftpContainer.SaveFile(ctx, localFile, remotePath)
require.NoError(t, err)
// Download a file
downloadPath := "/path/to/download/location.txt"
err = ftpContainer.GetFile(ctx, remotePath, downloadPath)
require.NoError(t, err)
// List files
entries, err := ftpContainer.ListFiles(ctx, "/")
require.NoError(t, err)
for _, entry := range entries {
fmt.Println(entry.Name, entry.Type) // Type: 0 for file, 1 for directory
}
// Delete a file
err = ftpContainer.DeleteFile(ctx, remotePath)
require.NoError(t, err)
}