gotests

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 3, 2025 License: Apache-2.0 Imports: 11 Imported by: 0

README

gotests License godoc Build Status Coverage Status codecov Go Report Card

gotests is a powerful Go test generator that automatically creates table-driven tests from your source code. It analyzes function and method signatures to generate comprehensive test scaffolding, saving you time and ensuring consistency across your test suite.

Key Features
  • 🚀 Zero-config test generation - Works out of the box with any Go project
  • 🎯 Smart test scaffolding - Generates complete table-driven test structure with proper type handling
  • 🔧 Flexible filtering - Generate tests for specific functions, exported functions, or entire packages
  • 📦 Auto-imports - Automatically adds required imports to test files
  • 🧬 Full generics support - Works seamlessly with Go 1.18+ type parameters
  • 🔄 Recursive generation - Process entire directory trees with ./... pattern
  • 🎨 Custom templates - Built-in support for testify and custom test templates
  • Parallel subtests - Optional parallel test execution support

Demo

The following shows gotests in action using the official Sublime Text 3 plugin. Plugins also exist for Emacs, also Emacs, Vim, Atom Editor, Visual Studio Code, and IntelliJ Goland.

demo

Installation

Minimum Go version: Go 1.22

Use go install to install and update:

$ go install github.com/cweill/gotests/gotests@latest

Usage

From the commandline, gotests can generate Go tests for specific source files or an entire directory. By default, it prints its output to stdout.

$ gotests [options] PATH ...

Available options:

  -all                  generate tests for all functions and methods

  -excl                 regexp. generate tests for functions and methods that don't
                         match. Takes precedence over -only, -exported, and -all

  -exported             generate tests for exported functions and methods. Takes
                         precedence over -only and -all

  -i                    print test inputs in error messages

  -named                switch table tests from using slice to map (with test name for the key)

  -only                 regexp. generate tests for functions and methods that match only.
                         Takes precedence over -all

  -nosubtests           disable subtest generation when >= Go 1.7

  -parallel             enable parallel subtest generation when >= Go 1.7.

  -w                    write output to (test) files instead of stdout

  -template_dir         Path to a directory containing custom test code templates. Takes
                         precedence over -template. This can also be set via environment
                         variable GOTESTS_TEMPLATE_DIR

  -template             Specify custom test code templates, e.g. testify. This can also
                         be set via environment variable GOTESTS_TEMPLATE

  -template_params_file read external parameters to template by json with file

  -template_params      read external parameters to template by json with stdin

  -use_go_cmp           use cmp.Equal (google/go-cmp) instead of reflect.DeepEqual

  -ai                   generate test cases using AI

  -ai-provider          AI provider: ollama, openai, claude, gemini (default "ollama")

  -ai-model             AI model to use (default depends on provider)

  -ai-endpoint          AI API endpoint (default depends on provider)

  -ai-api-key           API key for cloud AI providers (openai, claude, gemini).
                        Can also be set via GOTESTS_AI_API_KEY environment variable

  -ai-min-cases         minimum number of test cases to generate with AI (default 3)

  -ai-max-cases         maximum number of test cases to generate with AI (default 10)

  -version              print version information and exit

AI-Powered Test Generation

gotests can generate intelligent test cases using AI. This feature analyzes your function implementations and generates realistic test values, edge cases, and error conditions.

Supported AI Providers
Provider Type Models Pricing
Ollama Local qwen2.5-coder, llama3.2, codellama Free (runs locally)
OpenAI Cloud gpt-4o, gpt-4o-mini, gpt-4-turbo Pay-per-token
Claude Cloud claude-3-opus, claude-3-sonnet, claude-3-haiku Pay-per-token
Gemini Cloud gemini-1.5-pro, gemini-1.5-flash Pay-per-token
Any OpenAI-compatible Cloud/Local Various Varies
OpenAI-Compatible API Support

The openai provider works with any service that implements the OpenAI Chat Completions API format. This includes:

Service Endpoint Notes
OpenAI https://api.openai.com/v1 Default
Azure OpenAI https://{resource}.openai.azure.com/... Enterprise
Together AI https://api.together.xyz/v1 Open-source models
Groq https://api.groq.com/openai/v1 Ultra-fast inference
Anyscale https://api.endpoints.anyscale.com/v1 Open-source models
Fireworks AI https://api.fireworks.ai/inference/v1 Fast inference
DeepSeek https://api.deepseek.com/v1 Coding models
Mistral AI https://api.mistral.ai/v1 European provider
Perplexity https://api.perplexity.ai Search-augmented
OpenRouter https://openrouter.ai/api/v1 Multi-provider gateway
LM Studio http://localhost:1234/v1 Local GUI app
Quick Start with Ollama (Local, Free)
  1. Install Ollama (https://ollama.ai)

  2. Pull a model:

    ollama pull qwen2.5-coder:0.5b  # Small, fast model (400MB)
    # or
    ollama pull llama3.2:latest     # Larger, more capable (2GB)
    
  3. Generate tests with AI:

    gotests -all -ai -w yourfile.go
    
Quick Start with Cloud Providers
OpenAI
# Set your API key
export GOTESTS_AI_API_KEY="sk-..."

# Generate tests with GPT-4o-mini (fast and cheap)
gotests -all -ai -ai-provider openai -w yourfile.go

# Use a specific model
gotests -all -ai -ai-provider openai -ai-model gpt-4o -w yourfile.go
Any OpenAI-Compatible Provider
# Together AI
export GOTESTS_AI_API_KEY="your-together-key"
gotests -all -ai -ai-provider openai -ai-endpoint https://api.together.xyz/v1 -ai-model meta-llama/Llama-3-70b-chat-hf -w yourfile.go

# Groq (ultra-fast)
export GOTESTS_AI_API_KEY="your-groq-key"
gotests -all -ai -ai-provider openai -ai-endpoint https://api.groq.com/openai/v1 -ai-model llama-3.1-70b-versatile -w yourfile.go

# DeepSeek (coding specialist)
export GOTESTS_AI_API_KEY="your-deepseek-key"
gotests -all -ai -ai-provider openai -ai-endpoint https://api.deepseek.com/v1 -ai-model deepseek-coder -w yourfile.go

# LM Studio (local, no API key needed)
gotests -all -ai -ai-provider openai -ai-endpoint http://localhost:1234/v1 -ai-model local-model -w yourfile.go

# OpenRouter (access multiple providers)
export GOTESTS_AI_API_KEY="your-openrouter-key"
gotests -all -ai -ai-provider openai -ai-endpoint https://openrouter.ai/api/v1 -ai-model anthropic/claude-3-sonnet -w yourfile.go
Claude (Anthropic Native API)
# Set your API key
export GOTESTS_AI_API_KEY="sk-ant-..."

# Generate tests with Claude
gotests -all -ai -ai-provider claude -w yourfile.go

# Use a specific model
gotests -all -ai -ai-provider claude -ai-model claude-3-sonnet-20240229 -w yourfile.go
Gemini (Google Native API)
# Set your API key
export GOTESTS_AI_API_KEY="AIza..."

# Generate tests with Gemini
gotests -all -ai -ai-provider gemini -w yourfile.go

# Use a specific model
gotests -all -ai -ai-provider gemini -ai-model gemini-1.5-pro -w yourfile.go
Example

Given this function:

func CalculateDiscount(price float64, percentage int) (float64, error) {
    if price < 0 {
        return 0, errors.New("price cannot be negative")
    }
    if percentage < 0 || percentage > 100 {
        return 0, errors.New("percentage must be between 0 and 100")
    }
    discount := price * float64(percentage) / 100.0
    return price - discount, nil
}

The AI generates (showing 3 cases; by default, the AI generates between 3-10 cases):

func TestCalculateDiscount(t *testing.T) {
    type args struct {
        price      float64
        percentage int
    }
    tests := []struct {
        name    string
        args    args
        want    float64
        wantErr bool
    }{
        {
            name: "valid discount",
            args: args{price: 100.0, percentage: 20},
            want: 80.0,
            wantErr: false,
        },
        {
            name: "negative price",
            args: args{price: -10.0, percentage: 20},
            want: 0,
            wantErr: true,
        },
        {
            name: "invalid percentage",
            args: args{price: 100.0, percentage: 150},
            want: 0,
            wantErr: true,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := CalculateDiscount(tt.args.price, tt.args.percentage)
            if (err != nil) != tt.wantErr {
                t.Errorf("CalculateDiscount() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if got != tt.want {
                t.Errorf("CalculateDiscount() = %v, want %v", got, tt.want)
            }
        })
    }
}
AI Options
# Use Ollama with a different model
gotests -all -ai -ai-model llama3.2:latest -w yourfile.go

# Use OpenAI with API key from environment
export GOTESTS_AI_API_KEY="sk-..."
gotests -all -ai -ai-provider openai -ai-model gpt-4o -w yourfile.go

# Use Claude with API key as flag
gotests -all -ai -ai-provider claude -ai-api-key "sk-ant-..." -w yourfile.go

# Use Gemini
gotests -all -ai -ai-provider gemini -ai-api-key "AIza..." -w yourfile.go

# Generate a specific number of test cases (min = max)
gotests -all -ai -ai-min-cases 5 -ai-max-cases 5 -w yourfile.go

# Generate a range of test cases (AI chooses between 3-7)
gotests -all -ai -ai-min-cases 3 -ai-max-cases 7 -w yourfile.go

# Combine with other flags
gotests -exported -ai -parallel -w yourfile.go
Default Models by Provider
Provider Default Model Default Endpoint
Ollama qwen2.5-coder:0.5b http://localhost:11434
OpenAI gpt-4o-mini https://api.openai.com/v1
Claude claude-3-haiku-20240307 https://api.anthropic.com/v1
Gemini gemini-1.5-flash https://generativelanguage.googleapis.com/v1beta

Note: When using -ai-provider openai with a custom -ai-endpoint, you must also specify -ai-model as different providers support different models.

How It Works
  • Analyzes function implementation and logic
  • Generates realistic test values based on actual code
  • Creates test cases for edge cases and error conditions
  • Falls back to TODO comments if generation fails
  • Works offline with local models (privacy-first)
Supported Features

✅ Simple types (int, string, bool, float) ✅ Complex types (slices, maps, structs, pointers) ✅ Error returns and validation ✅ Variadic parameters ✅ Methods with receivers ✅ Multiple return values

Privacy & Security

What data is sent to the LLM:

  • Function signatures (name, parameters, return types)
  • Complete function bodies including all code and comments
  • No file paths or project context

Privacy considerations:

  • ⚠️ Function bodies may contain sensitive information - business logic, algorithms, or credentials/secrets in comments
  • Local-first by default - Using Ollama keeps all data on your machine; nothing is sent to external servers
  • Offline operation - AI generation works completely offline with local models
  • 🔒 Recommendation: Avoid using -ai on code containing secrets, API keys, or proprietary algorithms in comments

If using cloud providers in the future:

  • Function source code will be transmitted to the cloud provider's API
  • Review the provider's data retention and privacy policies
  • Consider using -ai only on non-sensitive codebases

Quick Start Examples

Generate tests for a single function

Given a file math.go:

package math

func Add(a, b int) int {
    return a + b
}

Generate a test:

$ gotests -only Add -w math.go

This creates math_test.go with:

func TestAdd(t *testing.T) {
    type args struct {
        a int
        b int
    }
    tests := []struct {
        name string
        args args
        want int
    }{
        // TODO: Add test cases.
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := Add(tt.args.a, tt.args.b); got != tt.want {
                t.Errorf("Add() = %v, want %v", got, tt.want)
            }
        })
    }
}
Generate tests for all exported functions
$ gotests -all -exported -w .

This generates tests for all exported functions in the current directory.

Generate tests recursively
$ gotests -all -w ./...

This generates tests for all functions in the current directory and all subdirectories.

Generate tests with testify
$ gotests -all -template testify -w calculator.go

This generates tests using the testify assertion library.

Common workflows
# Generate tests for all exported functions in a package
$ gotests -exported -w pkg/*.go

# Generate only tests for specific functions matching a pattern
$ gotests -only "^Process" -w handler.go

# Generate tests excluding certain functions
$ gotests -all -excl "^helper" -w utils.go

# Generate parallel subtests
$ gotests -all -parallel -w service.go

Go Generics Support

gotests fully supports Go generics (type parameters) introduced in Go 1.18+. It automatically generates tests for generic functions and methods on generic types.

Example: Generic Function

Given this generic function:

func FindFirst[T comparable](slice []T, target T) (int, error) {
    for i, v := range slice {
        if v == target {
            return i, nil
        }
    }
    return -1, ErrNotFound
}

Running gotests -all -w yourfile.go generates:

func TestFindFirst(t *testing.T) {
    type args struct {
        slice  []string
        target string
    }
    tests := []struct {
        name    string
        args    args
        want    int
        wantErr bool
    }{
        // TODO: Add test cases.
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := FindFirst[string](tt.args.slice, tt.args.target)
            if (err != nil) != tt.wantErr {
                t.Errorf("FindFirst() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if got != tt.want {
                t.Errorf("FindFirst() = %v, want %v", got, tt.want)
            }
        })
    }
}
Example: Methods on Generic Types

gotests also supports methods on generic types:

type Set[T comparable] struct {
    m map[T]struct{}
}

func (s *Set[T]) Add(v T) {
    if s.m == nil {
        s.m = make(map[T]struct{})
    }
    s.m[v] = struct{}{}
}

Generates:

func TestSet_Add(t *testing.T) {
    type args struct {
        v string
    }
    tests := []struct {
        name string
        s    *Set[string]
        args args
    }{
        // TODO: Add test cases.
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            s := &Set[string]{}
            s.Add(tt.args.v)
        })
    }
}
Type Constraint Mapping

gotests uses intelligent defaults for type parameter instantiation:

  • anyint
  • comparablestring
  • Union types (int64 | float64) → first option (int64)
  • Approximation constraints (~int) → underlying type (int)

This ensures generated tests use appropriate concrete types for testing generic code.

Contributions

Contributing guidelines are in CONTRIBUTING.md.

License

gotests is released under the Apache 2.0 License.

Documentation

Overview

Package gotests contains the core logic for generating table-driven tests.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type GeneratedTest

type GeneratedTest struct {
	Path      string             // The test file's absolute path.
	Functions []*models.Function // The functions with new test methods.
	Output    []byte             // The contents of the test file.
}

A GeneratedTest contains information about a test file with generated tests.

func GenerateTests

func GenerateTests(srcPath string, opt *Options) ([]*GeneratedTest, error)

GenerateTests generates table-driven tests for the function and method signatures defined in the target source path file(s). The source path parameter can be either a Go source file or directory containing Go files.

type Options

type Options struct {
	Only           *regexp.Regexp         // Includes only functions that match.
	Exclude        *regexp.Regexp         // Excludes functions that match.
	Exported       bool                   // Include only exported methods
	PrintInputs    bool                   // Print function parameters in error messages
	Subtests       bool                   // Print tests using Go 1.7 subtests
	Parallel       bool                   // Print tests that runs the subtests in parallel.
	Named          bool                   // Create Map instead of slice
	Importer       func() types.Importer  // A custom importer.
	Template       string                 // Name of custom template set
	TemplateDir    string                 // Path to custom template set
	TemplateParams map[string]interface{} // Custom external parameters
	TemplateData   [][]byte               // Data slice for templates
	UseGoCmp       bool                   // Use cmp.Equal (google/go-cmp) instead of reflect.DeepEqual
	UseAI          bool                   // Generate test cases using AI
	AIProvider     string                 // AI provider: ollama, openai, claude, gemini
	AIModel        string                 // AI model to use
	AIEndpoint     string                 // AI API endpoint
	AIAPIKey       string                 // API key for cloud AI providers
	AIMinCases     int                    // Minimum number of test cases to generate
	AIMaxCases     int                    // Maximum number of test cases to generate
}

Options provides custom filters and parameters for generating tests.

Directories

Path Synopsis
A commandline tool for generating table-driven Go tests.
A commandline tool for generating table-driven Go tests.
process
Package process is a thin wrapper around the gotests library.
Package process is a thin wrapper around the gotests library.
internal
ai
Package ai provides AI-powered test case generation for gotests.
Package ai provides AI-powered test case generation for gotests.
goparser
Package goparse contains logic for parsing Go files.
Package goparse contains logic for parsing Go files.

Jump to

Keyboard shortcuts

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