Golang 猴子补丁

Golang monkey patching

我知道如果 go 代码的结构使其被编程为接口,那么模拟就很简单了;但是,我正在使用一个我无法更改的代码库(不是我的),但事实并非如此。

这个代码库是高度互连的,没有任何东西被编程到接口,只有结构,所以没有依赖注入。

结构本身只包含其他结构,所以我也不能那样模拟。我不相信我可以对方法做任何事情,并且存在的少数函数不是变量,所以我不知道如何将它们换掉。继承在 golang 中不是一回事,所以这也是不行的。

在像 python 这样的脚本语言中,我们可以在运行时修改对象,也就是猴子补丁。我可以在 golang 中做一些类似的事情吗?试图在不触及底层代码的情况下找出一些方法 test/benchmark。

当我 运行 遇到这种情况时,我的方法是使用我自己的接口作为包装器,允许在测试中进行模拟。例如。

type MyInterface interface {
    DoSomething(i int) error
    DoSomethingElse() ([]int, error)
}

type Concrete struct {
    client *somepackage.Client
}

func (c *Concrete) DoSomething(i int) error {
    return c.client.DoSomething(i)
}

func (c *Concrete) DoSomethingElse() ([]int, error) {
    return c.client.DoSomethingElse()
}

如果 Concrete 也是一个接口,您现在可以像模拟 somepackage.Client 一样模拟 Concrete。

正如 @elithrar 在下面的评论中指出的那样,您可以嵌入要模拟的类型,这样您就只能被迫添加需要模拟的方法。例如:

type Concrete struct {
    *somepackage.Client
}

像那样完成后,可以直接在 Concrete 上调用 DoSomethingNotNeedingMocking 等其他方法,而无需将其添加到界面/模拟出来。

Go 有一个 available monkey patching library。它仅适用于 Intel/AMD 系统(特别针对 OSX 和 Ubuntu)。

根据情况,您可以应用 "Dependency Inversion Principle" 并利用 Go 的隐式接口。

要做到这一点,您需要在包中定义您的需求接口和使用方法(而不是定义您在实现它的包中提供的内容;就像您在 Java 中可能做的那样)。

然后您可以独立于依赖项测试您的代码。

具有结构依赖性的典型对象:

// Object that relies on a struct
type ObjectUnderTestBefore struct {
    db *sql.DB
}

func (o *ObjectUnderTestBefore) Delete() error {
    o.db.Exec("DELETE FROM sometable")
}

应用依赖倒置原则(带隐式接口)

// subset of sql.DB which defines our "requirements"
type dbExec interface {
    Exec(query string, args ...interface{}) (sql.Result, error)
}

// Same object with it's requirement defined as an local interface
type ObjectUnderTestWithDIP struct {
    // *sql.DB will implicitly implement this interface
    db dbExec
}

func (o *ObjectUnderTestWithDIP) Delete() error {
    o.db.Exec("DELETE FROM sometable")
}