tinymock

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 23 Imported by: 0

README

tinymock

Idiomatic mock generator for Go without any third-party runtime dependency.

//go:generate tinymock
type Starter interface {
	Start()
}

//go:generate tinymock
type Stopper interface {
	Stop()
}

//go:generate tinymock
type Engine interface {
	Starter
	Stopper
}

Just put go:generate tinymock directly before the interface you want to mock and run go generate to generate the mocks. That's it.

Installation

go install github.com/yonedash/tinymock/cmd/tinymock@latest

Usage

First, create an interface you want to mock. Then add a go:generate directive that runs tinymock. You don't need to configure anything else.

//go:generate tinymock
type Foo interface {
	Bar()
}

The go:generate tinymock directive must be placed directly before the interface declaration it targets.

Finally, run go generate to generate the mocks. If your source file is called myfile.go, all mocks from that file will be generated in myfile_mock.go. Each interface still needs its own go:generate tinymock directive.

The relevant part of the generated mock for our Foo example looks like this:

var _ Foo = FooMock{}

type FooMock struct {
	MockBar func()
}

func (m FooMock) Bar() {
	if m.MockBar == nil {
		panic("Foo.Bar is not mocked")
	}
	m.MockBar()
}

The generated mock is a lightweight test double: just ordinary Go code consisting of a struct with function fields and methods that call those functions. The var _ Foo = FooMock{} line makes the compiler verify that FooMock implements Foo.

Depending on how it is used in a test, the mock can act as a stub (by providing predefined behavior) or as a spy (by allowing the test to verify interactions manually). It does not provide built-in expectations, argument matching, call ordering, or automatic verification.

There is no runtime reflection, registration step, or separate configuration format to learn. You configure the mock by assigning functions to its fields:

mock := FooMock{
	MockBar: func() {
		// Test behavior here
	},
}

mock.Bar()

Each generated method has a corresponding function field on the mock. You only need to provide function fields for the methods used by your test. Calling an unconfigured method causes the mock to panic with a helpful message.

The generated *_mock.go files are managed by tinymock and should not be edited manually.

What is supported?

tinymock generates mocks (lightweight test doubles) for Go interfaces. It supports:

  • interface methods, including arguments and return values
  • embedded interfaces
  • embedded interfaces from other packages, such as io.Reader
  • generic interfaces

For example:

package example

import "io"

//go:generate tinymock
type Reader interface {
	io.Reader
}

tinymock generates a mock for the local Reader interface, including the methods provided by io.Reader.

Why another mocking tool?

I could not find a mocking tool that satisfied my inner zen. I don't like having potentially thousands of separate generated files scattered around my project when each file contains the mock for only one type. That's why tinymock can manage multiple mocks within the same file.

All mocks from foo.go are written to foo_mock.go, all mocks from bar.go are written to bar_mock.go, and so on. This keeps the number of generated files under control while still keeping the mocks close to the code they belong to.

I also don't like having to learn a separate configuration format or workflow just to generate simple mocks. They didn’t feel Go-like to me. With tinymock, the generation directive lives next to the interface it targets.

tinymock is intentionally small and focused. If you need expectations, call-order assertions, automatic argument matching, or other advanced mocking features, another tool may be a better fit.

If you want simple, idiomatic Go mocks without repeatedly writing the same boilerplate, tinymock is for you.

Documentation

Overview

This file provides utilities that are useful for testing.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FindTypeSpec

func FindTypeSpec(fset *token.FileSet, f *ast.File, lineNumber int) (*ast.TypeSpec, error)

Finds a type declaration by providing the line number. lineNumber must be a comment line of the desired type declaration.

func GenerateMock

func GenerateMock(s Struct) (src string, err error)

func MockFileName added in v0.1.0

func MockFileName(goFileName string) string

func Must

func Must[T any](v T, err error) T

If err is NOT nil, it will panic with err.

Types

type AlwaysFailReader

type AlwaysFailReader struct{}

AlwaysFailReader will always return an error when Read is called.

func (AlwaysFailReader) Read

func (r AlwaysFailReader) Read([]byte) (int, error)

type AlwaysFailWriter

type AlwaysFailWriter struct{}

AlwaysFailWriter will always return an error when Write is called.

func (AlwaysFailWriter) Write

func (r AlwaysFailWriter) Write([]byte) (int, error)

type Field

type Field struct {
	// If Name is an empty string, it is not a named field.
	// See `Named()`.
	Name string
	Type string
}

func PopulateNames

func PopulateNames(prefix string, fields []Field) (results []Field)

Populates names of fields sequentially to prefix0, prefix1, ..., prefixN. If a name is set for some fields, it will not be replaced. Name collisions are prevented by adjusting the prefix dynamically.

func (Field) Named

func (p Field) Named() bool

Returns true, if the field is an named field. Edge Case: If the fields name is "_", it counts a unnamed.

type Method

type Method struct {
	Name    string
	Params  []Field
	Results []Field
}

type MockFile

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

MockBuffer is buffer that contains the mock declarations of a .go file it is derived from.

func NewMockFile

func NewMockFile() *MockFile

func (*MockFile) Add

func (m *MockFile) Add(s Struct) error

func (*MockFile) Cleanup

func (m *MockFile) Cleanup(baseFileSrc []byte)

Deletes all entries which type names are not declared in the base file. baseFileSrc must be the source of the .go source file. It expects the baseFileSrc and scans it line by line to be as quick as possible.

func (*MockFile) Delete

func (m *MockFile) Delete(typeName string)

func (*MockFile) Modified

func (m *MockFile) Modified() bool

func (*MockFile) Parse

func (m *MockFile) Parse(r io.Reader) (err error)

func (*MockFile) Write

func (m *MockFile) Write(w io.Writer, packageName string) error

type MockFileEntry

type MockFileEntry struct {
	TypeName string
	Source   string
	Hash     string
	Imports  map[string]string
}

type Struct

type Struct struct {
	TypeName   string
	TypeParams []Field
	Methods    []Method
	// Maps package name to alias
	Imports map[string]string
}

func ParseDecl

func ParseDecl(fset *token.FileSet, ts *ast.TypeSpec, files ...*ast.File) (s Struct, err error)

Directories

Path Synopsis
cmd
tinymock command
internal

Jump to

Keyboard shortcuts

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