mocker

package
v0.44.0 Latest Latest
Warning

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

Go to latest
Published: Jan 14, 2026 License: MIT Imports: 17 Imported by: 0

README

Introduction

The mocker package provides a simple and flexible way to generate mock implementations for Go interfaces in tests. It automates the creation of mock structs that integrate with testing frameworks like testing.T and use github.com/ctx42/testing/pkg/mock as the main driver for the mocks. With mocker, you can generate mocks for interfaces in the same package or across different packages, writing the output to files, buffers, or other destinations.

Key features:

  • Automatic mock generation for any Go interface.
  • Configurable output (files, buffers, or custom writers).
  • Support for cross-package mocking with source and target package specifications.
  • Integration with testing utilities for robust test assertions.

Usage

Basic Mock Generation

To generate a mock for an interface in the same package, call Generate with the interface name. The mock is written to a default file (e.g., _mock.go) in the current package.

package main

import (
    "log"
    
    "github.com/ctx42/testing/pkg/mocker"
)

func main() {
    err := mocker.Generate("MyInterface")
    if err != nil {
        log.Fatalf("failed to generate mock: %v", err)
    }
}

This creates a mock file with a struct named MyInterfaceMock, including methods to record calls and integrate with testing.T.

Advanced Mock Generation

For more control, use configuration options to specify the source package, target package, and output destination. This example generates a mock for an interface in another package and writes it to a buffer:

out := &bytes.Buffer{}

err := mocker.Generate(
    "Case00",
    mocker.WithSrc("github.com/ctx42/testing/pkg/mocker/testdata/cases"),
    mocker.WithTgt("github.com/ctx42/testing/pkg/goldy"),
    mocker.WithTgtOutput(out),
)
if err != nil {
    panic(err)
}

fmt.Println(out.String())
// Output:
// package goldy
//
// // Code generated by mocker. DO NOT EDIT.
//
// import (
//	"github.com/ctx42/testing/pkg/mock"
//	"github.com/ctx42/testing/pkg/tester"
// )
//
// type Case00Mock struct {
//	*mock.Mock
//	t tester.T
// }
//
// func NewCase00Mock(t tester.T) *Case00Mock {
//	t.Helper()
//	return &Case00Mock{Mock: mock.NewMock(t), t: t}
// }
//
// func (_mck *Case00Mock) Method00() {
//	_mck.t.Helper()
//	var _args []any
//	_mck.Called(_args...)
// }

See examples_test.go for additional examples.

Configuration Options

The Generate function accepts optional configuration via option functions:

  • WithSrc(src string): the source package or directory. Defaults to the current package.
  • WithTgt(tgt string): the target package or directory for the generated mock. Defaults to the current package.
  • WithTgtOutput(w io.Writer): directs the mock output to the given writer. Defaults to a file named <interface>_mock.go.
  • WithTgtName(name string): customize mock type name. Defaults to TypeMock.
  • WithTgtFilename(filename string): customize mock filename.
  • WithTgtOnHelpers(): generate additional mock helper methods.
  • WithTesterAlias(alias string): sets alias for "github.com/ctx42/testing/pkg/tester" import.

Performance

Generating mocks involves resolving Go packages and parsing source files, which can be time-consuming due-to-disk I/O and AST parsing. These steps are particularly slow when generating mocks for many interfaces or interfaces in packages with many files.

To optimize performance, use a Mocker instance instead of the standalone Generate function. The Mocker struct maintains an in-memory cache of parsed packages and type information, reusing this data across multiple mock generations. This can significantly reduce execution time, especially in large projects or when generating mocks for multiple interfaces in the same package.

mck := mocker.New()

err := mck.Generate("ItfName0", mocker.WithSrc("src0"), mocker.WithTgt("tgt0"))
// Handle error.
err := mck.Generate("ItfName1", mocker.WithSrc("src1"), mocker.WithTgt("tgt1"))
// Handle error.
err := mck.Generate("ItfName2", mocker.WithSrc("src2"), mocker.WithTgt("tgt2"))
// Handle error.
err := mck.Generate("ItfName3", mocker.WithSrc("src3"), mocker.WithTgt("tgt3"))
// Handle error.

Go Generate

The mocker was designed to be used with Go’s go generate tool. By using go generate, you can embed mock generation directly into your build workflow without relying on external scripts or manual commands. This approach leverages Go’s standard tooling, keeping your project self-contained and idiomatic.

To use mocker with go generate, set up two files in the package where you want to generate mocks: one to trigger the generation and another to define the mock generation logic. This structure keeps the generation process organized and reusable.

Setup

Create the following files in your package directory (e.g., pkg/):

pkg/
  ├── 00_generate.go
  ├── 00_generate_main.go

The 00_ prefix ensures these files appear at the top of file listings for visibility, though any valid filename works. The files serve distinct purposes.

The 00_generate.go contains //go:generate directives to invoke the mock generation program.

package pkg

//go:generate go run 00_generate_main.go

The 00_generate_main.go defines a standalone program that calls mocker to create the mocks.

//go:build ignore

package main

import (
    "github.com/ctx42/testing/pkg/mocker"
)

func main() {
    err := mocker.Generate(
        "Case00",
        mocker.WithSrc("github.com/ctx42/testing/pkg/mocker/testdata/cases"),
        mocker.WithTgt("github.com/ctx42/testing/pkg/goldy"),
    )
    if err != nil {
        panic(err)
    }
}

The //go:build ignore ensures the file is not included in the package build, avoiding conflicts with the pkg package.

Generate

From the Root of your Go module, execute:

go generate ./...

This command scans all packages in the module for //go:generate directives and runs them. In this case, it executes go run 00_generate_main.go, generating the mock in the specified target package.

Documentation

Overview

Package mocker provides a tool for generating interface mocks.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrUnkPkg is returned when a directory or an import path does not point
	// to a valid Go package.
	//
	// This error occurs in the following cases:
	//   - The provided path cannot be resolved to a valid import path.
	//   - The import path cannot be resolved to a valid directory.
	ErrUnkPkg = errors.New("package not found")

	// ErrUnkType is returned when a type declaration cannot be found.
	ErrUnkType = errors.New("type not found")

	// ErrUnkItf is returned when an interface cannot be found.
	ErrUnkItf = errors.New("interface not found")

	// ErrUnkMet is returned when an interface method cannot be found.
	ErrUnkMet = errors.New("method not found")

	// ErrAstParse is returned when AST parsing encounters an error.
	ErrAstParse = errors.New("error parsing sources")

	// ErrNoMethods is returned when the interface to mock has no methods.
	ErrNoMethods = errors.New("interface has no methods")
)

Sentinel errors.

Functions

func Generate

func Generate(name string, opts ...Option) error

Generate creates a mock implementation for the specified interface name and writes it to the configured output.

Example
package main

import (
	"bytes"
	"fmt"

	"github.com/ctx42/testing/pkg/mocker"
)

func main() {
	out := &bytes.Buffer{}

	err := mocker.Generate(
		"Case00",
		mocker.WithSrc("testdata/cases"),
		mocker.WithTgt("."),
		mocker.WithTgtOutput(out),
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(out.String())
}
Output:

package mocker

// Code generated by mocker. DO NOT EDIT.

import (
	"github.com/ctx42/testing/pkg/mock"
	"github.com/ctx42/testing/pkg/tester"
)

type Case00Mock struct {
	*mock.Mock
	t tester.T
}

func NewCase00Mock(t tester.T) *Case00Mock {
	t.Helper()
	return &Case00Mock{Mock: mock.NewMock(t), t: t}
}

func (_mck *Case00Mock) Method00() {
	_mck.t.Helper()
	var _args []any
	_mck.Called(_args...)
}
Example (UsingImportPaths)
package main

import (
	"bytes"
	"fmt"

	"github.com/ctx42/testing/pkg/mocker"
)

func main() {
	out := &bytes.Buffer{}

	err := mocker.Generate(
		"Case00",
		mocker.WithSrc("github.com/ctx42/testing/pkg/mocker/testdata/cases"),
		mocker.WithTgt("github.com/ctx42/testing/pkg/goldy"),
		mocker.WithTgtOutput(out),
	)
	if err != nil {
		panic(err)
	}

	fmt.Println(out.String())
}
Output:

package goldy

// Code generated by mocker. DO NOT EDIT.

import (
	"github.com/ctx42/testing/pkg/mock"
	"github.com/ctx42/testing/pkg/tester"
)

type Case00Mock struct {
	*mock.Mock
	t tester.T
}

func NewCase00Mock(t tester.T) *Case00Mock {
	t.Helper()
	return &Case00Mock{Mock: mock.NewMock(t), t: t}
}

func (_mck *Case00Mock) Method00() {
	_mck.t.Helper()
	var _args []any
	_mck.Called(_args...)
}

func WithTgtOnHelpers

func WithTgtOnHelpers(cfg *Config)

WithTgtOnHelpers turns on "OnXXX" helper methods generation.

Types

type Config

type Config struct {
	// contains filtered or unexported fields
}

Config represents the configuration for the mocker.

type Mocker

type Mocker struct {
	// contains filtered or unexported fields
}

Mocker represents the main structure for creating interface mocks.

func New

func New() *Mocker

New creates a new Mocker instance.

func (*Mocker) Generate

func (mck *Mocker) Generate(name string, opts ...Option) error

Generate creates a mock implementation for the specified interface name and writes it to the configured output.

type Option

type Option func(*Config)

Option represents a mocker option.

func WithSrc

func WithSrc(dirOrImp string) Option

WithSrc sets the source directory or import path (package) where the interface to mock is defined.

func WithTesterAlias added in v0.30.0

func WithTesterAlias(alias string) Option

WithTesterAlias sets the alias for the "github.com/ctx42/testing/pkg/tester" package. When the alias is set to empty string, it will use "_tester" as the alias.

func WithTgt

func WithTgt(dirOrImp string) Option

WithTgt sets the target directory or import path (package) where the interface to mock should be created.

func WithTgtFilename

func WithTgtFilename(filename string) Option

WithTgtFilename sets the filename to write the generated interface mock to.

func WithTgtName

func WithTgtName(name string) Option

WithTgtName sets the interface mock type name.

func WithTgtOutput

func WithTgtOutput(w io.Writer) Option

WithTgtOutput configures the writer for the generated interface output. It takes precedence over the WithTgtFilename option. If the provided writer implements io.Closer, its Close method will be called after writing.

Jump to

Keyboard shortcuts

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