MCPcopy Index your code
hub / github.com/bytedance/mockey

github.com/bytedance/mockey @v1.4.6

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.4.6 ↗ · + Follow
606 symbols 1,724 edges 100 files 103 documented · 17% 8 cross-repo links updated 2mo agov1.4.6 · 2026-04-17★ 9003 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
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!")     // assert
        })

        // mock released
        PatchConvey("mock released", func() {
            res := Foo("anything")               // invoke
            So(res, ShouldEqual, "ori:anything") // assert
        })

        // mock again
        PatchConvey("mock 2", func() {
            Mock(Foo).Return("MOCKED-2!").Build() // mock
            res := Foo("anything")                // invoke
            So(res, ShouldEqual, "MOCKED-2!")     // assert
        })
    })

    // Tips: Like `Convey`, `PatchConvey` can be nested; each layer of `PatchConvey` will only release its own internal mocks
}

PatchRun example as follows:

package main_test

import (
    "testing"

    . "github.com/bytedance/mockey"
)

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

func TestXXX(t *testing.T) {
    // mock
    PatchRun(func() {
        Mock(Foo).Return("MOCKED-1!").Build() // mock
        res := Foo("anything")                // call
        if res != "MOCKED-1!" {
            t.Errorf("expected 'MOCKED-1!', got '%s'", res)
        }
    })

    // mock released
    res := Foo("anything") // call
    if res != "ori:anything" {
        t.Errorf("expected 'ori:anything', got '%s'", res)
    }

    // mock again
    PatchRun(func() {
        Mock(Foo).Return("MOCKED-2!").Build() // mock
        res := Foo("anything")                // call
        if res != "MOCKED-2!" {
            t.Errorf("expected 'MOCKED-2!', got '%s'", res)
        }
    })

    // mock released
    res = Foo("anything") // call
    if res != "ori:anything" {
        t.Errorf("expected 'ori:anything', got '%s'", res)
    }
}

Providing GetMethod to handle special cases

In special cases where direct mocking is not possible or not effective, you can use GetMethod to get the corresponding method before mocking. Please ensure that the passed object is not nil.

Mock method through an instance (including interface type instances):

package main

import (
    "fmt"

    . "github.com/bytedance/mockey"
)

type A struct{}

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

func main() {
    a := new(A)

    // Mock(a.Foo) won't work, because `a` is an instance of `A`, not the type `A`
    // Tips: if the instance is an interface type, you can use it the same way
    // var ia interface{ Foo(string) string } = new(A)
    // Mock(GetMethod(ia, "Foo")).Return("MOCKED!").Build()
    Mock(GetMethod(a, "Foo")).Return("MOCKED!").Build()
    fmt.Println(a.Foo("anything")) // MOCKED!
}

Mock method of unexported types:

package main

import (
    "crypto/sha256"
    "fmt"

    . "github.com/bytedance/mockey"
)

func main() {
    // `sha256.New()` returns an unexported `*digest`, whose `Sum` method is the one we want to mock
    Mock(GetMethod(sha256.New(), "Sum")).Return([]byte{0}).Build()

    fmt.Println(sha256.New().Sum([]byte("anything"))) // [0]

    // Tips: this is a special case of "mocking methods through instances", where the type corresponding to the instance is unexported
}

Mock unexported method:

package main

import (
    "bytes"
    "fmt"

    . "github.com/bytedance/mockey"
)

func main() {
    // `*bytes.Buffer` has an unexported `empty` method, which is the one we want to mock
    Mock(GetMethod(new(bytes.Buffer), "empty")).Return(true).Build()

    buf := bytes.NewBuffer([]byte{1, 2, 3, 4})
    b, err := buf.ReadByte()
    fmt.Println(b, err)      // 0 EOF | `ReadByte` calls `empty` method inside to check if the buffer is empty and return io.EOF
}

The Go compiler may directly erase the type information of unexported methods, in which case GetMethod will fail to retrieve the method. In such cases, you can use OptUnexportedTargetType to explicitly specify its type:

package main

import (
    "bytes"
    "fmt"

    . "github.com/bytedance/mockey"
)

func main() {
    var targetType func() bool // Signature of the unexported `empty` method (excluding receiver)
    target := GetMethod(new(bytes.Buffer), "empty", OptUnexportedTargetType(targetType))
    Mock(target).Return(true).Build()

    buf := bytes.NewBuffer([]byte{1, 2, 3, 4})
    b, err := buf.ReadByte()
    fmt.Println(b, err) // 0 EOF | `ReadByte` internally calls the `empty` method to check if the buffer is empty and returns io.EOF
}

Mock methods in neste

Extension points exported contracts — how you extend this code

MyI (Interface)
(no doc) [3 implementers]
exp/iface/mock_test.go
SequenceOpt (Interface)
(no doc) [1 implementers]
mock_sequence.go
TestI (Interface)
(no doc) [1 implementers]
mock_test.go
Analyzer (Interface)
(no doc) [1 implementers]
internal/fn/analyzer.go
Fn (FuncType)
(no doc)
utils_test.go
Selector (Interface)
(no doc) [3 implementers]
exp/iface/internal/selector.go
OptionFn (FuncType)
(no doc)
exp/iface/option.go

Core symbols most depended-on inside this repo

Build
called by 167
mock.go
PatchConvey
called by 155
global.go
Return
called by 104
mock.go
Assert
called by 88
internal/tool/assert.go
Mock
called by 86
mock.go
To
called by 84
mock.go
GetMethod
called by 78
utils.go
DebugPrintf
called by 53
internal/tool/debug.go

Shape

Function 303
Method 199
Struct 78
TypeAlias 13
Interface 9
FuncType 4

Languages

Go100%

Modules by API surface

mock_generics_test.go66 symbols
mock.go38 symbols
utils_test.go33 symbols
mock_test.go29 symbols
internal/unsafereflect/type.go26 symbols
exp/iface/mock_test.go23 symbols
internal/monkey/inst/disasm_arm64.go17 symbols
internal/fn/analyzer.go16 symbols
exp/iface/internal/selector.go16 symbols
utils_1_18_test.go15 symbols
internal/monkey/inst/disasm_amd64.go15 symbols
mock_var.go14 symbols

For agents

$ claude mcp add mockey \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact