Skip to content

axpira/gt

Repository files navigation

gt (Go Test)

Go Report Card GoDoc Coverage Status

gt is a minimalist, idiomatic testing library for Go. It provides powerful assertions and a lightweight spying mechanism without the bloat of heavy frameworks.

Designed for Effective Go:

  • Zero Dependencies: Pure Go standard library.
  • No Magic: Explicit, readable, and type-safe.
  • Extensible: Plug in your favorite diff tools (like go-cmp) or JSON comparators.

Installation

go get github.com/axpira/gt

Quick Start

gt works alongside the standard testing package.

package main_test

import (
	"errors"
	"testing"

	"github.com/axpira/gt"
)

func TestExample(t *testing.T) {
	got := 2 + 2
	gt.Equal(t, "math check", 4, got)

	err := errors.New("something went wrong")
	target := errors.New("something went wrong")
	
	// Checks if err matches target (using errors.Is)
	gt.ErrorIs(t, "error check", err, target)
}

Advanced Usage

Custom Diff Function

By default, gt uses reflect.DeepEqual. You can easily plug in google/go-cmp for better diffs:

import "github.com/google/go-cmp/cmp"

// ... inside test
gt.Equal(t, "complex struct", want, got, gt.WithDiffFunc(func(want, got any) string {
    if diff := cmp.Diff(want, got); diff != "" {
        return diff
    }
    return ""
}))

JSON Comparison

Compare JSON strings or bytes ignoring whitespace:

gt.Equal(t, "json check", 
    []byte(`{"id": 1, "name": "foo"}`), 
    []byte(`{
        "name": "foo",
        "id": 1
    }`), 
    gt.WithJSONDiff,
)

Custom Failure Behavior

Change how gt handles failures (default is FailNow):

// Don't stop execution on failure
gt.Equal(t, "soft check", 1, 2, gt.WithFailLazy())

// Custom hook (e.g., for logging)
gt.Equal(t, "hook check", 1, 2, gt.WithFailHook(func() {
    t.Log("Assertion failed!")
}))

Mocking & Spying

gt includes a spy package to facilitate Manual Mocking. Instead of generating complex mock code, we encourage defining simple mock structs that record their interactions.

The spy.Recorder

The spy.Recorder is a thread-safe helper that records function calls and arguments.

Step-by-Step Example

1. Define the Interface

type Repository interface {
    Save(ctx context.Context, data string) error
}

2. Create the Mock Embed spy.Recorder in your struct.

import "github.com/axpira/gt/spy"

type MockRepo struct {
    spy.Recorder
    
    // Configurable return values
    SaveErr error
}

func (m *MockRepo) Save(ctx context.Context, data string) error {
    // Record the call. 
    // It automatically captures the method name "Save" and arguments.
    m.Record(ctx, data) 
    return m.SaveErr
}

3. Use in Test

func TestService_Save(t *testing.T) {
    // Setup Mock
    mock := &MockRepo{SaveErr: nil}
    
    // Execute logic
    err := mock.Save(context.Background(), "important data")
    
    // Assertions
    gt.ErrorIs(t, "save error", err, nil)
    
    // Verify Interactions
    // History is [][]any: [[MethodName, Arg1, Arg2...], [MethodName, ...]]
    
    gt.Equal(t, "call count", 1, len(mock.History))
    
    lastCall := mock.History[0]
    gt.Equal(t, "method name", "Save", lastCall[0])
    gt.Equal(t, "argument", "important data", lastCall[2]) // [0]=Name, [1]=ctx, [2]=data
}

Why Manual Mocks?

  • Type Safety: Compiler checks your mocks.
  • Refactoring: Rename methods and your mocks update automatically (mostly).
  • Simplicity: No code generation steps or complex DSLs.

License

MIT License. See LICENSE file for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages