Completely type-safe compile-time mock generator for Go

PkgGoDev Go Report Card

Mockc

Mockc is a completely type-safe compile-time mock generator for Go. You can use it just by writing the mock generators with mockc.Implement() or using it with command like flags.

Check out my blog post for more details.

Features

  • Tools
    • Generating mock with mock generators
    • Generating mock with command line flags (experimental feature)
  • Generated Mock
    • Capturing params and results of the method
    • Capturing method calls
    • Injecting method body
    • Customizing mock's field names with the prefix and the suffix
      • default: prefix:"_", suffix:""
    • Generating mock constructor

Installation

go get github.com/KimMachineGun/mockc/cmd/mockc

Look and Feel

You can see more examples here.

Target Interface

package basic

type Cache interface {
	Get(key string) (val interface{}, err error)
	Set(key string, val interface{}) (err error)
	Del(key string) (err error)
}

If you want to generate mock that implements the above interface, follow the steps below.

With Mock Generator

1. Write Mock Generator

If you want to generate mock with mock generator, write a mock generator first. The mock will be generated in its generator path, and it'll be named its generator's name. You can write multiple generators in one file, and multiple mocks will be generated. The mock generator should be consisted of function calls of the mockc package.

//+build mockc

package basic

import (
	"github.com/KimMachineGun/mockc"
)

func MockcCache() {
	mockc.Implement(Cache(nil))
}

If you want to customize the field names of the mock, use mockc.SetFieldNamePrefix() or mockc.SetFieldNameSuffix(). (Notice: These functions only work with constant string value.)

2. Generate Mock

This command will generate mock with your mock generator. The <package-pattern> argument will be used for loading mock generator with golang.org/x/tools/go/packages#Load. If it's not provided, . will be used.

mockc [<packages-pattern>]
Ex: mock ./example

With Command Line Flags

1. Generate Mock

This command will generate mock with its command line flags. If you generate mock with this command, you don't need to write the mock generator. The <target-interface-pattern> should follow {package_path}.{interface_name} format.

mockc -destination=<output-file> -name=<mock-name> [-withConstructor] [-fieldNamePrefix=<prefix>] [-fieldNameSuffix=<suffix>] <target-interface-pattern> [<target-interface-pattern>]
Ex: mockc -destination=./example/mockc_gen.go -name=MockcCache github.com/KimMachineGun/mockc/example.Cache

If you want to customize the field names of the mock, pass string value to the -fieldNamePrefix or -fieldNameSuffix.

Generated Mock

The //go:generate directive may vary depending on your mock generation command.

// Code generated by Mockc. DO NOT EDIT.
// repo: https://github.com/KimMachineGun/mockc

//go:generate mockc
// +build !mockc

package basic

import "sync"

var _ interface {
	Cache
} = &MockcCache{}

type MockcCache struct {
	// method: Del
	_Del struct {
		mu sync.Mutex
		// basics
		Called    bool
		CallCount int
		// call history
		History []struct {
			Params struct {
				P0 string
			}
			Results struct {
				R0 error
			}
		}
		// params
		Params struct {
			P0 string
		}
		// results
		Results struct {
			R0 error
		}
		// if it is not nil, it'll be called in the middle of the method.
		Body func(string) error
	}
	// method: Get
	_Get struct {
		mu sync.Mutex
		// basics
		Called    bool
		CallCount int
		// call history
		History []struct {
			Params struct {
				P0 string
			}
			Results struct {
				R0 interface{}
				R1 error
			}
		}
		// params
		Params struct {
			P0 string
		}
		// results
		Results struct {
			R0 interface{}
			R1 error
		}
		// if it is not nil, it'll be called in the middle of the method.
		Body func(string) (interface{}, error)
	}
	// method: Set
	_Set struct {
		mu sync.Mutex
		// basics
		Called    bool
		CallCount int
		// call history
		History []struct {
			Params struct {
				P0 string
				P1 interface{}
			}
			Results struct {
				R0 error
			}
		}
		// params
		Params struct {
			P0 string
			P1 interface{}
		}
		// results
		Results struct {
			R0 error
		}
		// if it is not nil, it'll be called in the middle of the method.
		Body func(string, interface{}) error
	}
}

func (recv *MockcCache) Del(p0 string) error {
	recv._Del.mu.Lock()
	defer recv._Del.mu.Unlock()
	// basics
	recv._Del.Called = true
	recv._Del.CallCount++
	// params
	recv._Del.Params.P0 = p0
	// body
	if recv._Del.Body != nil {
		recv._Del.Results.R0 = recv._Del.Body(p0)
	}
	// call history
	recv._Del.History = append(recv._Del.History, struct {
		Params struct {
			P0 string
		}
		Results struct {
			R0 error
		}
	}{
		Params:  recv._Del.Params,
		Results: recv._Del.Results,
	})
	// results
	return recv._Del.Results.R0
}

func (recv *MockcCache) Get(p0 string) (interface{}, error) {
	recv._Get.mu.Lock()
	defer recv._Get.mu.Unlock()
	// basics
	recv._Get.Called = true
	recv._Get.CallCount++
	// params
	recv._Get.Params.P0 = p0
	// body
	if recv._Get.Body != nil {
		recv._Get.Results.R0, recv._Get.Results.R1 = recv._Get.Body(p0)
	}
	// call history
	recv._Get.History = append(recv._Get.History, struct {
		Params struct {
			P0 string
		}
		Results struct {
			R0 interface{}
			R1 error
		}
	}{
		Params:  recv._Get.Params,
		Results: recv._Get.Results,
	})
	// results
	return recv._Get.Results.R0, recv._Get.Results.R1
}

func (recv *MockcCache) Set(p0 string, p1 interface{}) error {
	recv._Set.mu.Lock()
	defer recv._Set.mu.Unlock()
	// basics
	recv._Set.Called = true
	recv._Set.CallCount++
	// params
	recv._Set.Params.P0 = p0
	recv._Set.Params.P1 = p1
	// body
	if recv._Set.Body != nil {
		recv._Set.Results.R0 = recv._Set.Body(p0, p1)
	}
	// call history
	recv._Set.History = append(recv._Set.History, struct {
		Params struct {
			P0 string
			P1 interface{}
		}
		Results struct {
			R0 error
		}
	}{
		Params:  recv._Set.Params,
		Results: recv._Set.Results,
	})
	// results
	return recv._Set.Results.R0
}

Feel Free to Use the Generated Mock

package basic

import (
	"errors"
	"testing"
)

func HasKey(c Cache, key string) (bool, error) {
	val, err := c.Get(key)
	if err != nil {
		return false, err
	}

	return val != nil, nil
}

func TestHasKey(t *testing.T) {
	m := &MockcCache{}

	// set return value
	m._Get.Results.R0 = struct{}{}

	// execute
	key := "test_key"
	result, err := HasKey(m, key)

	// assert
	if !result {
		t.Error("result should be true")
	}
	if err != nil {
		t.Error("err should be nil")
	}
	if m._Get.CallCount != 1 {
		t.Errorf("Cache.Get should be called once: actual(%d)", m._Get.CallCount)
	}
	if m._Get.Params.P0 != key {
		t.Errorf("Cache.Get should be called with %q: actual(%q)", key, m._Get.Params.P0)
	}
}

func TestHasKey_WithBodyInjection(t *testing.T) {
	m := &MockcCache{}

	// inject body
	key := "test_key"
	m._Get.Body = func(actualKey string) (interface{}, error) {
		if actualKey != key {
			t.Errorf("Cache.Get should be called with %q: actual(%q)", key, actualKey)
		}
		return nil, errors.New("error")
	}

	// execute
	result, err := HasKey(m, key)

	// assert
	if result {
		t.Error("result should be false")
	}
	if err == nil {
		t.Error("err should not be nil")
	}
	if m._Get.CallCount != 1 {
		t.Errorf("Cache.Get should be called once: actual(%d)", m._Get.CallCount)
	}
}

Inspired by 🙏

Similar Resources

ESME is a go library that allows you to mock a RESTful service by defining the configuration in json format

ESME is a go library that allows you to mock a RESTful service by defining the configuration in json format. This service can then simply be consumed by any client to get the expected response.

Mar 2, 2021

mockery - A mock code autogenerator for Golang

mockery - A mock code autogenerator for Golang

mockery - A mock code autogenerator for Golang

Jan 8, 2023

Create your own mock server with just a JSON file!

Gmocker Run a blazing fast mock server in just seconds! 🚀 All you need is to make a json file that contains path and response mapping. See an example

Dec 21, 2022

Create your own blazing fast mock server with just a JSON file!

Gmocker Run a blazing fast mock server in just seconds! 🚀 All you need is to make a json file that contains path and response mapping. See an example

Dec 21, 2022

Apple Push Notification service (APNs) mock server

APNs Mock Server Server

Dec 8, 2022

Merge Mock - testing tool for the Ethereum Merge

MergeMock Experimental debug tooling, mocking the execution engine and consensus node for testing. work in progress Quick Start To get started, build

Oct 21, 2022

A basic lightweight HTTP client for Go with included mock features.

A basic lightweight HTTP client for Go with included mock features. Features Support almost all http method like G

May 2, 2022

📡 mock is a simple, cross-platform, cli app to simulate HTTP-based APIs.

 📡 mock is a simple, cross-platform, cli app to simulate HTTP-based APIs.

mock 📡 mock is a simple, cross-platform, cli app to simulate HTTP-based APIs. About mock Mock allows you to spin up a local http server based of a .m

May 6, 2022

A mock of Go's net package for unit/integration testing

netmock: Simulate Go network connections netmock is a Go package for simulating net connections, including delays and disconnects. This is work in pro

Oct 27, 2021
Comments
  • mockc CLI  error: destination file should go file: _

    mockc CLI error: destination file should go file: _

    command

    mockc -name=DynamoDBMock -destination=pkg/repositories/dynamoaa.go github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface.DynamoDBAPI

    error message

    destination file should be go file: _
    
Vault mock - Mock of Hashicorp Vault used for unit testing

vault_mock Mock of Hashicorp Vault used for unit testing Notice This is a person

Jan 19, 2022
Mock-the-fck - Mock exercise for human

Mock the fck Originally, Mockery-example Example case for mockery issue #128 fil

Jan 21, 2022
Stupidly fast, completely serverless and infinitely scalable stress tester.
Stupidly fast, completely serverless and infinitely scalable stress tester.

dontstress Stupidly fast, completely serverless and infinitely scalable stress tester. Don't stress over your failing competitive programming task sol

Nov 15, 2021
A tool for generating self-contained, type-safe test doubles in go

counterfeiter When writing unit-tests for an object, it is often useful to have fake implementations of the object's collaborators. In go, such fake i

Jan 5, 2023
Package cdp provides type-safe bindings for the Chrome DevTools Protocol (CDP), written in the Go programming language.

cdp Package cdp provides type-safe bindings for the Chrome DevTools Protocol (CDP), written in the Go programming language. The bindings are generated

Jan 7, 2023
Type-safe assertion helpers for Go

attest attest is a small package of type-safe assertion helpers. Under the hood, it uses cmp for equality testing and diffing. You may enjoy attest if

Aug 30, 2022
Sql mock driver for golang to test database interactions

Sql driver mock for Golang sqlmock is a mock library implementing sql/driver. Which has one and only purpose - to simulate any sql driver behavior in

Dec 31, 2022
HTTP mock for Golang: record and replay HTTP/HTTPS interactions for offline testing

govcr A Word Of Warning I'm in the process of partly rewriting govcr to offer better support for cassette mutations. This is necessary because when I

Dec 28, 2022
Powerful mock generation tool for Go programming language

Summary Minimock generates mocks out of Go interface declarations. The main features of minimock are: It generates statically typed mocks and helpers.

Dec 17, 2022
Mock object for Go http.ResponseWriter

mockhttp -- Go package for unit testing HTTP serving Unit testing HTTP services written in Go means you need to call their ServeHTTP receiver. For thi

Sep 27, 2022