SkillAgentSearch skills...

Mockey

A simple and easy-to-use Go mocking library derived from ByteDance's internal best practices

Install / Use

/learn @bytedance/Mockey
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Mockey

English | 中文

Release License Go Report Card codecov OpenIssue ClosedIssue

Mockey is a simple and easy-to-use golang mock library, which can quickly and conveniently mock functions and variables. At present, it is widely used in the unit test writing of ByteDance services (7k+ repos) and is actively maintained. In essence, it rewrites function instructions at runtime similarly to monkey or gomonkey.

Mockey makes it easy to replace functions, methods and variables with mocks reducing the need to specify all dependencies as interfaces.

  1. Mockey requires inlining and compilation optimization to be disabled during compilation, or it won't work. See the FAQs for details.
  2. It is strongly recommended to use it together with the goconvey library in unit tests.

Install

go get github.com/bytedance/mockey@latest

Quick Guide

Simplest example

package main

import (
	"fmt"
	"math/rand"

	. "github.com/bytedance/mockey"
)

func main() {
	Mock(rand.Int).Return(1).Build() // mock `rand.Int` to return 1
	
	fmt.Printf("rand.Int() always return: %v\n", rand.Int()) // Try if it's still random?
}

Unit test example

package main_test

import (
	"math/rand"
	"testing"

	. "github.com/bytedance/mockey"
	. "github.com/smartystreets/goconvey/convey"
)

// Win function to be tested, input a number, win if it's greater than random number, otherwise lose
func Win(in int) bool {
	return in > rand.Int()
}

func TestWin(t *testing.T) {
	PatchConvey("TestWin", t, func() {
		Mock(rand.Int).Return(100).Build() // mock
		
		res1 := Win(101)                   // execute
		So(res1, ShouldBeTrue)             // assert
		
		res2 := Win(99)         // execute
		So(res2, ShouldBeFalse) // assert
	})
}

Features

  • Mock functions and methods
    • Basic
      • Simple / generic / variadic function or method (value or pointer receiver)
      • Supporting hook function
      • Supporting PatchConvey and PatchRun (automatically release mocks after each test case)
      • Providing GetMethod to handle special cases (e.g., unexported types, unexported method, and methods in nested structs)
    • Advanced
      • Interface mocking (experimental feature)
      • Conditional mocking
      • Sequence returning
      • Decorator pattern (execute the original function after mocking)
      • Goroutine filtering (inclusion, exclusion, targeting)
      • Acquire Mocker for advanced usage (e.g., getting the execution times of target/mock function)
  • Mock variable
    • Common variable
    • Function variable

Compatibility

OS Support

  • Mac OS(Darwin)
  • Linux
  • Windows

Arch Support

  • AMD64
  • ARM64

Version Support

  • Go 1.13+

Basic Features

Simple function/method

Use Mock to mock function/method, use Return to specify the return value, and use Build to make the mock effective:

package main

import (
	"fmt"

	. "github.com/bytedance/mockey"
)

func Foo(in string) string {
	return in
}

type A struct{}

func (a A) Foo(in string) string { return in }

type B struct{}

func (b *B) Foo(in string) string { return in }

func main() {
	// mock function
	Mock(Foo).Return("MOCKED!").Build()
	fmt.Println(Foo("anything")) // MOCKED!

	// mock method (value receiver)
	Mock(A.Foo).Return("MOCKED!").Build()
	fmt.Println(A{}.Foo("anything")) // MOCKED!

	// mock method (pointer receiver)
	Mock((*B).Foo).Return("MOCKED!").Build()
	fmt.Println(new(B).Foo("anything")) // MOCKED!
    
    // Tips: if the target has no return value, you still need to call the empty `Return()` or use `To` to customize the hook function.
}

Generic function/method

Starting from v1.3.0, Mock experimentally adds the ability to automatically identify generics (for go1.20+), you can use Mock to directly replace MockGeneric

Use MockGeneric to mock generic function/method:

package main

import (
	"fmt"

	. "github.com/bytedance/mockey"
)

func FooGeneric[T any](t T) T {
	return t
}

type GenericClass[T any] struct {
}

func (g *GenericClass[T]) Foo(t T) T {
	return t
}

func main() {
	// mock generic function 
	MockGeneric(FooGeneric[string]).Return("MOCKED!").Build() // `Mock(FooGeneric[string], OptGeneric)` also works
	fmt.Println(FooGeneric("anything"))                       // MOCKED!
	fmt.Println(FooGeneric(1))                                // 1 | Not working because of type mismatch!

	// mock generic method 
	MockGeneric((*GenericClass[string]).Foo).Return("MOCKED!").Build()
	fmt.Println(new(GenericClass[string]).Foo("anything")) // MOCKED!
}

Additionally, Golang generics share implementation for different types with the same underlying type. For example, in type MyString string, MyString and string share one implementation. Therefore, mocking one type will interfere with the other.

package main

import (
	"fmt"

	. "github.com/bytedance/mockey"
)

type MyString string

func FooGeneric[T any](t T) T {
	return t
}

func main() {
	MockGeneric(FooGeneric[string]).Return("MOCKED!").Build()
	fmt.Println(FooGeneric("anything"))           // MOCKED!
	fmt.Println(FooGeneric[MyString]("anything")) // MOCKED! | This is due to interference after mocking the string type
}

In v1.3.1, this issue was resolved. We now support mocking generic functions/methods with the same gcshape but different actual types. The example above will behave more as expected:

package main

import (
	"fmt"

	. "github.com/bytedance/mockey"
)

type MyString string

func FooGeneric[T any](t T) T {
	return t
}

func main() {
	mocker1 := MockGeneric(FooGeneric[string]).Return("MOCKED!").Build()
	fmt.Println(FooGeneric("anything"))           // MOCKED!
	fmt.Println(FooGeneric[MyString]("anything")) // anything | No longer interferes
	mocker2 := MockGeneric(FooGeneric[MyString]).Return("MOCKED2!").Build()
	fmt.Println(FooGeneric("anything"))           // MOCKED!
	fmt.Println(FooGeneric[MyString]("anything")) // MOCKED2! | No longer interferes

	// Note: If you need to manually release mockers, be sure to follow the "last-in-first-out" order, otherwise unexpected results such as crashes may occur
	mocker2.UnPatch()
	fmt.Println(FooGeneric("anything"))           // MOCKED!
	fmt.Println(FooGeneric[MyString]("anything")) // anything
	mocker1.UnPatch()
	fmt.Println(FooGeneric("anything"))           // anything
	fmt.Println(FooGeneric[MyString]("anything")) // anything
}

Variadic function/method

package main

import (
	"fmt"

	. "github.com/bytedance/mockey"
)

func FooVariadic(in ...string) string {
	return in[0]
}

type A struct{}

func (a A) FooVariadic(in ...string) string { return in[0] }

func main() {
	// mock variadic function
	Mock(FooVariadic).Return("MOCKED!").Build()
	fmt.Println(FooVariadic("anything")) // MOCKED!

	// mock variadic method
	Mock(A.FooVariadic).Return("MOCKED!").Build()
	fmt.Println(A{}.FooVariadic("anything")) // MOCKED!
}

Supporting hook function

Use To to specify the hook function:

package main

import (
	"fmt"

	. "github.com/bytedance/mockey"
)

func Foo(in string) string {
	return in
}

type A struct {
	prefix string
}

func (a A) Foo(in string) string { return a.prefix + ":" + in }

func main() {
	// NOTE: hook function must have the same function signature as the original function!
	Mock(Foo).To(func(in string) string { return "MOCKED!" }).Build()
	fmt.Println(Foo("anything")) // MOCKED!

	// NOTE: for method mocking, the receiver can be added to the signature of the hook function on your need (if the receiver is not used, it can be omitted, and mockey is compatible).
	Mock(A.Foo).To(func(a A, in string) string { return a.prefix + ":inner:" + "MOCKED!" }).Build()
	fmt.Println(A{prefix: "prefix"}.Foo("anything")) // prefix:inner:MOCKED!
}

Supporting PatchConvey and PatchRun

Starting from v1.4.1, PatchRun is supported

PatchConvey and PatchRun are tools for managing mock lifecycles. They automatically release mocks after test cases or functions are executed, eliminating the need for defer. Both support nested usage, and each layer only releases its own internal mocks.

Applicable scenarios comparison:

  • When you need to use the assertion features and test organization capabilities of the goconvey framework, it is recommended to use PatchConvey; the execution order of nested PatchConvey is the same as Convey, please refer to the goconvey related documentation
  • When you don't need goconvey integration, it is recommended to use the more lightweight PatchRun

PatchConvey example as follows:

package main_test

import (
	"testing"

	. "github.com/bytedance/mockey"
	. "github.com/smartystreets/goconvey/convey"
)

func Foo(in string) string {
	return "ori:" + in
}

func TestXXX(t *testing.T) {
	PatchConvey("TestXXX", t, func() {
		// mock
		PatchConvey("mock 1", func() {
			Mock(Foo).Return("MOCKED-1!").Build() // mock
			res := Foo("anything")                // invoke
			So(res, ShouldEqual, "MOCKED-1!")     // as

Related Skills

View on GitHub
GitHub Stars885
CategoryDevelopment
Updated3d ago
Forks41

Languages

Go

Security Score

100/100

Audited on Mar 21, 2026

No findings