got

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 25, 2025 License: Apache-2.0 Imports: 8 Imported by: 0

README

Got - A Fluent Go Testing Framework

Go Version Version License

English | 中文


Overview

Got is a comprehensive testing framework for Go applications that provides a fluent API for writing expressive and readable tests. It offers built-in support for test cases, assertions, error handling, and mock utilities for Redis and SQL databases.

Features

  • 🚀 Fluent API - Write expressive and readable tests with method chaining
  • 🎯 Built-in Assertions - Comprehensive assertion methods for common testing scenarios
  • 📊 Table-driven Tests - Support for structured test cases with the Case interface
  • 🔧 Error Handling - Utilities for testing error conditions
  • 🗄️ Mock Support - Built-in mock utilities for Redis and SQL databases
  • 🎨 Smart Output - Color-coded terminal output with graceful fallback
  • Performance - Lightweight and fast execution

Installation

go get github.com/go4x/got

Quick Start

Basic Usage
package main

import (
    "testing"
    "github.com/go4x/got"
)

func TestCalculator(t *testing.T) {
    r := got.New(t, "Calculator Tests")

    r.Case("Testing addition")
    result := 2 + 3
    r.Require(result == 5, "2 + 3 should equal 5")

    r.Case("Testing division by zero")
    _, err := divide(10, 0)
    r.AssertErrf(err, "Division by zero should return error")
}
Table-driven Tests
func TestStringLength(t *testing.T) {
    r := got.New(t, "String Length Tests")
    
    cases := []got.Case{
        got.NewCase("Valid Input", "hello", 5, false, nil),
        got.NewCase("Empty Input", "", 0, false, nil),
    }
    
    r.Cases(cases, func(c got.Case, tt *testing.T) {
        result := len(c.Input().(string))
        r.Require(result == c.Want().(int), "Length should match expected")
    })
}
Enhanced Assertions
func TestEnhancedAssertions(t *testing.T) {
    r := got.New(t, "Enhanced Assertions")
    
    // Equality assertions
    r.AssertEqual(5, 5, "Numbers should be equal")
    r.AssertNotEqual(5, 6, "Numbers should not be equal")
    
    // Nil assertions
    r.AssertNil(nil, "Value should be nil")
    r.AssertNotNil("hello", "Value should not be nil")
    
    // Boolean assertions
    r.AssertTrue(true, "Condition should be true")
    r.AssertFalse(false, "Condition should be false")
    
    // Contains assertions
    r.AssertContains("hello world", "world", "String should contain substring")
    r.AssertNotContains("hello world", "foo", "String should not contain substring")
}

API Reference

Core Methods
Test Runner
  • New(t *testing.T, title string) *R - Create a new test runner
  • Case(format string, args ...any) *R - Start a new test case
  • Run(name string, f func(t *testing.T)) *R - Execute a subtest
  • Cases(cases []Case, f func(c Case, tt *testing.T)) - Run table-driven tests
Assertions
  • Require(cond bool, desc string, args ...any) - Basic boolean assertion
  • FailNow(cond bool, desc string, args ...any) - Critical assertion that stops on failure
  • AssertEqual(expected, actual any, msg ...string) *R - Equality assertion
  • AssertNotEqual(expected, actual any, msg ...string) *R - Inequality assertion
  • AssertNil(value any, msg ...string) *R - Nil assertion
  • AssertNotNil(value any, msg ...string) *R - Non-nil assertion
  • AssertTrue(condition bool, msg ...string) *R - True assertion
  • AssertFalse(condition bool, msg ...string) *R - False assertion
  • AssertContains(container, item any, msg ...string) *R - Contains assertion
  • AssertNotContains(container, item any, msg ...string) *R - Not contains assertion
Error Handling
  • AssertNoErr(err error) - Assert no error
  • AssertNoErrf(err error, desc string, args ...any) - Assert no error with description
  • AssertErr(err error) - Assert error exists
  • AssertErrf(err error, desc string, args ...any) - Assert error with description
Utility Methods
  • Pass(format string, args ...any) - Log success message
  • Fail(format string, args ...any) - Log failure message
  • Fatal(format string, args ...any) - Log fatal error and stop
  • StartTimer() *R - Start timing
  • StopTimer() *R - Stop timing and log duration
  • Parallel() *R - Mark test as parallel
  • Skip(reason string, args ...any) *R - Skip test
  • Cleanup(fn func()) *R - Register cleanup function
Mock Utilities
Redis Mock
import "github.com/go4x/got/redist"

// Mock Redis client
client, mock := redist.MockRedis()

// Mini Redis for testing
client, err := redist.NewMiniRedis()
SQL Mock
import "github.com/go4x/got/sqlt"

// Create SQL mock
mockDB, err := sqlt.NewSqlmock()

// Create GORM mock
gormMock, err := mockDB.Gorm()

Advanced Features

Environment Variables
  • NO_COLOR=1 - Disable color output
  • TERM=dumb - Use text-only output
Color Support

The framework automatically detects terminal color support and provides:

  • Color-coded output in supported terminals
  • Graceful fallback to text labels in unsupported environments
Performance Monitoring
func TestPerformance(t *testing.T) {
    r := got.New(t, "Performance Test")
    
    r.StartTimer()
    // Your test code here
    r.StopTimer()
    
    r.MemoryUsage()
    r.GoroutineCount()
    r.TestInfo()
}

Examples

Check out the examples for more comprehensive usage patterns.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Changelog

v1.0.0
  • Initial release
  • Fluent API for Go testing
  • Built-in assertion methods
  • Redis and SQL mock utilities
  • Smart color output with fallback
  • Comprehensive test coverage

Documentation

Overview

Package got provides a comprehensive testing framework for Go applications.

Package got provides a comprehensive testing framework for Go applications. It offers a fluent API for writing expressive and readable tests with built-in support for test cases, assertions, and error handling.

The package includes:

  • Test runner with fluent API for organizing test cases
  • Built-in assertion methods for common testing scenarios
  • Support for table-driven tests with structured test cases
  • Error handling utilities for testing error conditions
  • Mock utilities for Redis and SQL databases

Example usage:

func TestCalculator(t *testing.T) {
	r := got.New(t, "Calculator Tests")

	r.Case("Testing addition")
	result := 2 + 3
	r.Require(result == 5, "2 + 3 should equal 5")

	r.Case("Testing division by zero")
	_, err := divide(10, 0)
	r.Errf(err, "Division by zero should return error")
}

For more examples, see the example functions in this package.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CaseBuilder

func CaseBuilder(name string) *caseBuilder

CaseBuilder creates a new case builder for fluent test case construction. This provides a builder pattern for creating test cases with method chaining.

Parameters:

  • name: The initial name for the test case

Returns:

  • *caseBuilder: A new case builder instance

Example:

case := got.CaseBuilder("Test Case").
	Input("hello").
	Want(5).
	WantErr(false).
	Err(nil).
	Build()

Types

type Case

type Case interface {
	Namer

	Input() any    // the input of the test case
	Want() any     // the expected result of the test case
	WantErr() bool // whether the test case should return an error
	Err() error    // the error of the test case
}

Case defines the structure of a test case for table-driven tests. It provides a standardized way to define test inputs, expected outputs, and error conditions for multiple test scenarios.

The interface includes:

  • Name(): A descriptive name for the test case
  • Input(): The input data for the test
  • Want(): The expected output/result
  • WantErr(): Whether the test case should produce an error
  • Err(): The specific error expected (if WantErr() is true)

Example:

case := got.NewCase("Valid Login", "[email protected]", true, false, nil)
// case.Name() returns "Valid Login"
// case.Input() returns "[email protected]"
// case.Want() returns true
// case.WantErr() returns false
// case.Err() returns nil

func NewCase

func NewCase(name string, input any, want any, wantErr bool, err error) Case

NewCase creates a new test case with the provided parameters. This is the simplest way to create a test case for table-driven tests.

Parameters:

  • name: A descriptive name for the test case
  • input: The input data for the test
  • want: The expected output/result
  • wantErr: Whether the test case should produce an error
  • err: The specific error expected (if wantErr is true)

Returns:

  • Case: A new test case instance

Example:

case := got.NewCase("Valid Input", "hello", 5, false, nil)
case := got.NewCase("Invalid Input", "", 0, true, errors.New("empty input"))

type Namer

type Namer interface {
	Name() string
}

Namer is an interface for objects that have a name. This is used by the Case interface to provide naming functionality.

type R

type R struct {
	*testing.T
	// contains filtered or unexported fields
}

R represents a test runner that provides a fluent API for writing tests. It embeds *testing.T to provide all standard testing functionality while adding enhanced logging, assertion methods, and test case management.

The runner maintains state for:

  • title: The main test suite title
  • caseNum: Current case number for automatic numbering
  • prefix: Formatted prefix for case logging
  • startTime: Test start time for timing
  • benchmark: Whether running in benchmark mode
  • parallel: Whether test is marked as parallel

Example:

r := got.New(t, "My Test Suite")
r.Case("First test case")
r.Require(condition, "Description")

func New

func New(t *testing.T, title string) *R

New creates a new test runner instance from a testing.T. The title parameter is used to identify the test suite in logs and output.

Parameters:

  • t: The testing.T instance from the test function
  • title: A descriptive title for the test suite

Returns:

  • *R: A new test runner instance

Example:

func TestMyFeature(t *testing.T) {
	r := got.New(t, "My Feature Tests")
	// Use r for test cases and assertions
}
Example
// This example shows how to create a new test runner
// In a real test function, you would do:
// func TestExample(t *testing.T) {
//     r := New(t, "Example")
//     // Output: Test Case => Example
// }
fmt.Println("Test Case => Example")
fmt.Println("&{Example 0  <nil> <nil>}")
Output:

Test Case => Example
&{Example 0  <nil> <nil>}

func (*R) AssertContains

func (r *R) AssertContains(container, item any, msg ...string) *R

AssertContains provides a more descriptive contains assertion

func (*R) AssertEqual

func (r *R) AssertEqual(expected, actual any, msg ...string) *R

AssertEqual provides a more descriptive equality assertion

func (*R) AssertErr

func (r *R) AssertErr(err error)

AssertErr checks if the provided error is not nil. If err is not nil, it passes the test; otherwise, it fails with a default message. This is useful for testing error conditions where you expect an error to occur.

Parameters:

  • err: The error to check

Example:

_, err := divide(10, 0)
r.AssertErr(err) // Expects an error for division by zero

func (*R) AssertErrf

func (r *R) AssertErrf(err error, desc string, args ...any)

AssertErrf checks if the provided error is not nil with a custom description. If err is not nil, it passes the test with the given description. If err is nil, it fails the test and stops execution. This method is useful when you need to provide context about what error was expected.

Parameters:

  • err: The error to check
  • desc: A description of what error was expected
  • args: Arguments for the description format string

Example:

_, err := validateInput("")
r.AssertErrf(err, "Empty input should cause validation error")

func (*R) AssertFalse

func (r *R) AssertFalse(condition bool, msg ...string) *R

AssertFalse provides a more descriptive false assertion

func (*R) AssertNil

func (r *R) AssertNil(value any, msg ...string) *R

AssertNil provides a more descriptive nil assertion

func (*R) AssertNoErr

func (r *R) AssertNoErr(err error)

AssertNoErr checks if the provided error is nil. If err is nil, it passes the test; otherwise, it fails with a default message. This is a convenience method for the common case of checking for no error.

Parameters:

  • err: The error to check

Example:

_, err := someFunction()
r.AssertNoErr(err)

func (*R) AssertNoErrf

func (r *R) AssertNoErrf(err error, desc string, args ...any)

AssertNoErrf checks if the provided error is nil with a custom description. If err is nil, it passes the test with the given description. If err is not nil, it fails the test, logs the error details, and stops execution. This method is useful when you need to provide context about what operation failed.

Parameters:

  • err: The error to check
  • desc: A description of what operation should not have failed
  • args: Arguments for the description format string

Example:

user, err := authenticateUser(username, password)
r.AssertNoErrf(err, "User authentication should succeed for %s", username)

func (*R) AssertNotContains

func (r *R) AssertNotContains(container, item any, msg ...string) *R

AssertNotContains provides a more descriptive not-contains assertion

func (*R) AssertNotEqual

func (r *R) AssertNotEqual(expected, actual any, msg ...string) *R

AssertNotEqual provides a more descriptive inequality assertion

func (*R) AssertNotNil

func (r *R) AssertNotNil(value any, msg ...string) *R

AssertNotNil provides a more descriptive non-nil assertion

func (*R) AssertNotPanics

func (r *R) AssertNotPanics(fn func(), msg ...string) *R

AssertNotPanics provides a more descriptive no-panic assertion

func (*R) AssertPanics

func (r *R) AssertPanics(fn func(), msg ...string) *R

AssertPanics provides a more descriptive panic assertion

func (*R) AssertTrue

func (r *R) AssertTrue(condition bool, msg ...string) *R

AssertTrue provides a more descriptive true assertion

func (*R) Benchmark

func (r *R) Benchmark(name string, f func(b *testing.B)) *R

Benchmark starts a benchmark test

func (*R) Case

func (r *R) Case(format string, args ...any) *R

Case starts a new test case with a descriptive message. It automatically increments the case number and logs the case description. The method supports printf-style formatting for dynamic case descriptions.

Parameters:

  • format: A format string describing the test case
  • args: Arguments for the format string

Returns:

  • *R: The runner instance for method chaining

Example:

r.Case("Testing user authentication with valid credentials")
r.Case("Testing division by zero with divisor %d", 0)
Example
// This example shows how to use Case method
// In a real test function, you would do:
// func TestCase(t *testing.T) {
//     r := New(t, "Example Case")
//     r.Case("this is a test case: %d", 1)
// }
fmt.Println("Test Case => Example Case")
fmt.Println("Case 1 -> this is a test case: 1")
Output:

Test Case => Example Case
Case 1 -> this is a test case: 1

func (*R) Caser

func (r *R) Caser(name string, f func(t *testing.T)) *R

Caser runs a test case with the given name and function. It combines Case and Run methods to create a named subtest with automatic case logging. This is useful for organizing related test scenarios under a common name.

Parameters:

  • name: The name of the test case
  • f: The test function to execute

Returns:

  • *R: The runner instance for method chaining

Example:

r.Caser("Valid Login", func(t *testing.T) {
	// Test valid login scenario
	r.Require(login("user", "pass"), "Login should succeed")
})
Example
// This example shows how to use Caser method
// In a real test function, you would do:
// func TestCaser(t *testing.T) {
//     r := New(t, "Example Caser")
//     r.Caser("should pass", func(tt *testing.T) {
//         r.Pass("pass in subtest")
//     })
// }

func (*R) Cases

func (r *R) Cases(cases []Case, f func(c Case, tt *testing.T))

Cases runs a set of test cases, executing the provided function for each case. This method is designed for table-driven tests where you have multiple test scenarios with different inputs and expected outputs.

For each test case, it:

  • Logs the case description using Case()
  • Runs the case as a subtest using Run()
  • Passes the case data to the test function

Parameters:

  • cases: A slice of Case implementations containing test data
  • f: The test function that will be executed for each case

Example:

cases := []got.Case{
	got.NewCase("Valid Input", "hello", 5, false, nil),
	got.NewCase("Empty Input", "", 0, false, nil),
}
r.Cases(cases, func(c got.Case, tt *testing.T) {
	result := len(c.Input().(string))
	r.Require(result == c.Want().(int), "Length should match expected")
})
Example
// This example shows how to use Cases method
// In a real test function, you would do:
// func TestCases(t *testing.T) {
//     r := New(t, "Example Cases")
//     cases := []Case{
//         NewCase("case1", 1, 1, false, nil),
//         NewCase("case2", 2, 3, false, nil),
//     }
//     r.Cases(cases, func(c Case, tt *testing.T) {
//         if c.Input() == c.Want() {
//             r.Pass("input equals want")
//         } else {
//             r.Fail("input not equal want")
//         }
//     })
// }

func (*R) Cleanup

func (r *R) Cleanup(fn func()) *R

Cleanup registers a cleanup function

func (*R) Deadline

func (r *R) Deadline() (deadline time.Time, ok bool)

Deadline returns the time when the test will be timed out

func (*R) Fail

func (r *R) Fail(format string, args ...any)

Fail logs a failed assertion with a red X mark. Use this method to indicate that a test condition has failed. This method will mark the test as failed but will not stop execution.

Parameters:

  • format: A format string describing the failed assertion
  • args: Arguments for the format string

Example:

r.Fail("User authentication should have succeeded")
r.Fail("Value %d is outside expected range", 100)
Example
// This example shows how to use Fail method
// In a real test function, you would do:
// func TestFail(t *testing.T) {
//     r := New(t, "Example Fail")
//     r.Fail("failed: %s", "error")
// }
fmt.Println("Test Case => Example Fail")
fmt.Println("\t✗ failed: error")
Output:

Test Case => Example Fail
	✗ failed: error

func (*R) FailNow

func (r *R) FailNow(cond bool, desc string, args ...any)

FailNow checks a boolean condition and stops test execution if it fails. If the condition is true, it logs a pass message and continues. If the condition is false, it logs a fail message and immediately stops the test. This is useful for critical assertions that should halt the test if they fail.

Parameters:

  • cond: The boolean condition to check
  • desc: A description of what is being tested
  • args: Arguments for the description format string

Example:

r.FailNow(db.IsConnected(), "Database connection is required for this test")
r.FailNow(config.IsValid(), "Configuration must be valid to continue")

func (*R) Fatal

func (r *R) Fatal(format string, args ...any)

Fatal logs a fatal error and immediately stops test execution. This method is equivalent to calling Fail() followed by t.FailNow(). Use this when a test cannot continue due to a critical failure.

Parameters:

  • format: A format string describing the fatal error
  • args: Arguments for the format string

Example:

r.Fatal("Database connection failed - cannot continue test")
r.Fatal("Critical system component %s is not available", "auth-service")

func (*R) Getenv

func (r *R) Getenv(key string) string

Getenv gets an environment variable

func (*R) GoroutineCount

func (r *R) GoroutineCount() *R

GoroutineCount logs the current goroutine count

func (*R) Helper

func (r *R) Helper() *R

Helper marks the calling function as a test helper function

func (*R) MemoryUsage

func (r *R) MemoryUsage() *R

MemoryUsage logs memory usage information

func (*R) Parallel

func (r *R) Parallel() *R

Parallel marks the test as safe to run in parallel

func (*R) Pass

func (r *R) Pass(format string, args ...any)

Pass logs a successful assertion with a green checkmark. Use this method to indicate that a test condition has passed.

Parameters:

  • format: A format string describing the successful assertion
  • args: Arguments for the format string

Example:

r.Pass("User authentication succeeded")
r.Pass("Value %d is within expected range", 42)
Example
// This example shows how to use Pass method
// In a real test function, you would do:
// func TestPass(t *testing.T) {
//     r := New(t, "Example Pass")
//     r.Pass("passed: %s", "ok")
// }
fmt.Println("Test Case => Example Pass")
fmt.Println("\t✓ passed: ok")
Output:

Test Case => Example Pass
	✓ passed: ok

func (*R) Require

func (r *R) Require(cond bool, desc string, args ...any)

Require is a convenient assertion method that checks a boolean condition. If the condition is true, it logs a pass message; otherwise, it logs a fail message. This is the most commonly used assertion method for simple boolean checks.

Parameters:

  • cond: The boolean condition to check
  • desc: A description of what is being tested
  • args: Arguments for the description format string

Example:

r.Require(user.IsAuthenticated(), "User should be authenticated")
r.Require(len(items) > 0, "Items list should not be empty")
r.Require(result == expected, "Result %d should equal %d", result, expected)
Example
// This example shows how to use Require method
// In a real test function, you would do:
// func TestRequire(t *testing.T) {
//     r := New(t, "Example Require")
//     r.Require(true, "should pass")
//     r.Require(false, "should fail")
// }
fmt.Println("Test Case => Example Require")
fmt.Println("\t✓ should pass")
fmt.Println("\t✗ should fail")
Output:

Test Case => Example Require
	✓ should pass
	✗ should fail

func (*R) Run

func (r *R) Run(name string, f func(t *testing.T)) *R

Run executes a subtest with the given name and function. It wraps testing.T.Run to provide a convenient way to run subtests while maintaining the fluent API pattern.

Parameters:

  • name: The name of the subtest
  • f: The test function to execute

Returns:

  • *R: The runner instance for method chaining

Example:

r.Run("Database Connection", func(t *testing.T) {
	// Test database connection
	conn, err := connectDB()
	r.NoErrf(err, "Database connection should succeed")
})

func (*R) RunParallel

func (r *R) RunParallel(fn func(*testing.PB)) *R

RunParallel runs tests in parallel

func (*R) Setenv

func (r *R) Setenv(key, value string) *R

Setenv sets an environment variable for the test

func (*R) Skip

func (r *R) Skip(reason string, args ...any) *R

Skip skips the current test with a reason

func (*R) SkipIf

func (r *R) SkipIf(condition bool, reason string, args ...any) *R

SkipIf skips the test if the condition is true

func (*R) SkipUnless

func (r *R) SkipUnless(condition bool, reason string, args ...any) *R

SkipUnless skips the test unless the condition is true

func (*R) StartTimer

func (r *R) StartTimer() *R

StartTimer starts timing the test

func (*R) StopTimer

func (r *R) StopTimer() *R

StopTimer stops timing and logs the duration

func (*R) TempDir

func (r *R) TempDir() string

TempDir returns a temporary directory for the test

func (*R) TestInfo

func (r *R) TestInfo() *R

TestInfo logs comprehensive test information

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL