如何在golang中模拟函数

时间:2017-12-04 22:42:56

标签: go mocking

我写了一个简单的包,它基本上由很多getter函数组成。此包中的每个文件对应一个服务,因此例如产品文件包含与产品服务/ db相关的函数,订单文件以订购服务等。每个函数将db资源作为参数作为参数,并将参数作为参数sql,例如。 productid,name,orderid。每个函数都返回一个结构(例如Order,product)或错误:

// product.go

package lib

type Product struct {
   ID int
   Name string
   Price float
}

func GetProductById(DB *sql.DB, ID int) (p Product, err error) {
   q := "SELECT * FROM product WHERE id = " + ID
   ...
}

func GetProductByName(DB *sql.DB, name string) (p Product, err error) {
   ...
}

// order.go

package lib

type Order struct {
   ID int
   Date string
   Items []items
}

func GetOrderById(DB *sql.DB, ID int) (o Order, err error) {
   ...
}

问题在于我无法从主程序包中模拟这些功能。我真正喜欢做的是重写包,所以我可以以某种方式将函数传递给一个类型。但我不知道该怎么做。特别是当函数采用不同的输入参数并返回不同的结构时。有没有办法做到这一点?

1 个答案:

答案 0 :(得分:5)

在Go中你不能模拟一个函数声明,与具体类型的方法声明相同,你不能嘲笑它们。

例如:

func F()

func (T) M()

FM在Go中无法模仿。

但是,您可以模拟函数值,无论它们是变量,结构上的字段还是传递给其他函数的参数。

例如:

var Fn = func() { ... }

type S struct {
    Fn func()
}

func F(Fn func())
在这三种情况下,

Fn都是可以模仿的。

你可以在Go中模拟的另一件事,以及大部分时间的首选选项,都是interface

例如:

type ProductRepository interface {
    GetProductById(DB *sql.DB, ID int) (p Product, err error)
}

// the real implementater of the interface
type ProductStore struct{}

func (ProductStore) GetProductById(DB *sql.DB, ID int) (p Product, err error) {
    q := "SELECT * FROM product WHERE id = " + ID
    // ...
}

// the mock implementer
type ProductRepositoryMock struct {}

func (ProductRepositoryMock) GetProductById(DB *sql.DB, ID int) (p Product, err error) {
    // ...
}

现在,当您处于"正常模式"时,任何依赖于ProductRepository的代码都可以传递ProductStore类型的值。

时,您需要输入ProductRepositoryMock类型的值。

使用interface的另一个选项,它允许你保持你的函数大多数没有变化是定义一个模仿*sql.DB的方法然后使用该接口类型的接口要传递给函数的类型,实现该接口的模拟版本并在测试期间使用它。

例如:

type DBIface interface {
    Query(query string, args ...interface{}) (*sql.Rows, error)
    // ...
    // It's enough to implement only those methods that
    // the functions that depend on DBIface actually use.
    // If none of your functions ever calls SetConnMaxLifetime
    // you don't need to declare that method on the DBIface type.
}

type DBMock struct {}

func (DBMock) Query(query string, args ...interface{}) (*sql.Rows, error) {
    // ...
}

func GetProductByName(DB DBIface, name string) (p Product, err error) {
   ...
}

DB参数GetProductByName现在可以模仿。