Skip to content

go-pkgz/testutils

Repository files navigation

testutils Build Status Go Report Card Coverage Status Go Reference

Package testutils provides useful test helpers.

Details

Capture Utilities

  • CaptureStdout: Captures stdout output from the provided function
  • CaptureStderr: Captures stderr output from the provided function
  • CaptureStdoutAndStderr: 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.

File Utilities

  • WriteTestFile: Creates a temporary file with specified content and returns its path. The file is automatically cleaned up after the test completes.

HTTP Test Utilities

  • 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.

Test Containers

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 creation
  • MySQLTestContainer: MySQL database container with automatic database creation
  • MongoTestContainer: 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)

Install and update

go get -u github.com/go-pkgz/testutils

Example Usage

Capture Functions

// 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)
}

File Utilities

// 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
}

HTTP Test Utilities

// 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))
}

Test Containers

// 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)
}

About

Testing helpers

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Contributors 2

  •  
  •  

Languages

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy