bytedance / mockey

a simple and easy-to-use golang mock library
Apache License 2.0
556 stars 22 forks source link
golang mock test testing

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. The bottom layer is monkey patch realized by rewriting function instructions at runtime.

  1. inlining and compilation optimization need to be disabled during compilation, otherwise mock may fail or an error may be reported. See the following FAQ chapter for details.
  2. In the actual process of writing a unit test, it is recommended to use it together with the Convey library.

Install

go get github.com/bytedance/mockey@latest

Quick Guide

import (
    "fmt"
    "testing"

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

func Foo(in string) string {
    return in
}

type A struct{}

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

var Bar = 0

func TestMockXXX(t *testing.T) {
    PatchConvey("TestMockXXX", t, func() {
        Mock(Foo).Return("c").Build()   // mock function
        Mock(A.Foo).Return("c").Build() // mock method
        MockValue(&Bar).To(1)           // mock variable

        So(Foo("a"), ShouldEqual, "c")        // assert `Foo` is mocked
        So(new(A).Foo("b"), ShouldEqual, "c") // assert `A.Foo` is mocked
        So(Bar, ShouldEqual, 1)               // assert `Bar` is mocked
    })
    // mock is released automatically outside `PatchConvey`
    fmt.Println(Foo("a"))        // a
    fmt.Println(new(A).Foo("b")) // b
    fmt.Println(Bar)             // 0
}

Features

Compatibility

OS Support

License

Mockey is distributed under the Apache License, version 2.0. The licenses of third party dependencies of Mockey are explained here.

FAQ

How to disable inline and compile optimization?

  1. Command line:go test -gcflags="all=-l -N" -v ./...
  2. Goland:fill -gcflags="all=-l -N" in the Run/Debug Configurations > Go tool arguments dialog box

The original function is still entered after mocking?

  1. Inline or compilation optimization is not disabled: you can try to use the debug mode. If you can run through it, it means that it is the problem. Please go to the relevant section of FAQ
  2. The Build() method was not called: forgot to call Build(), resulting in no actual effect
  3. Target function does not match exactly:

    func TestXXX(t *testing.T) {
    Mock((*A).Foo).Return("c").Build()
    fmt.Println(A{}.Foo("a")) // enters the original function, because the mock target should be `A.Foo`
    
    a := A{}
    Mock(a.Foo).Return("c").Build()
    fmt.Println(a.Foo("a")) // enters the original function, because the mock target should be `A.Foo` or extracted from instance `a` using `GetMethod`
    }
  4. The target function is executed in other goroutines:
    func TestXXX(t *testing.T) {
    PatchConvey("TestXXX", t, func() {
      Mock(Foo).Return("c").Build()
      go Foo("a") // the timing of executing 'foo' is uncertain
    })
    // when the main goroutine comes here, the relevant mock has been released by 'PatchConvey'. If 'foo' is executed before this, the mock succeeds, otherwise it fails
    fmt.Println("over")
    time.Sleep(time.Second)
    }

Error "function is too short to patch"?

  1. Inline or compilation optimization is not disabled: you can try to use the debug mode. If you can run through it, it means that it is the problem. Please go to the relevant section of FAQ
  2. The function is really too short: it means that the target function is less than one line, resulting in the compiled machine code being too short. Generally, two or more lines will not cause this problem
  3. Repeat mocking the same function: repeat mocking the same function in the PatchConvey of the smallest unit. If there is such a need, please get the Mocker instance and remock.
  4. Other tools mock this function: for example, monkey or other tools have mocked this function