Mockey
A simple and easy-to-use Go mocking library derived from ByteDance's internal best practices
Install / Use
/learn @bytedance/MockeyREADME
Mockey
English | 中文
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.
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
PatchConveyandPatchRun(automatically release mocks after each test case) - Providing
GetMethodto 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
Mockerfor advanced usage (e.g., getting the execution times of target/mock function)
- Basic
- 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,
Mockexperimentally adds the ability to automatically identify generics (for go1.20+), you can useMockto directly replaceMockGeneric
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,
PatchRunis 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 nestedPatchConveyis the same asConvey, 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
gh-issues
335.2kFetch GitHub issues, spawn sub-agents to implement fixes and open PRs, then monitor and address PR review comments. Usage: /gh-issues [owner/repo] [--label bug] [--limit 5] [--milestone v1.0] [--assignee @me] [--fork user/repo] [--watch] [--interval 5] [--reviews-only] [--cron] [--dry-run] [--model glm-5] [--notify-channel -1002381931352]
node-connect
335.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
xurl
335.2kA CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.
frontend-design
82.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
