如何在Golang中模拟函数 [英] How to mock functions in golang
问题描述
我写了一个简单的程序包,它基本上由许多getter函数组成.此软件包中的每个文件都对应一个服务,例如产品文件,包含与产品服务/数据库,订购文件到订购服务等相关的功能.每个功能都将基础数据库的db资源以及用于以下参数的参数作为参数.的SQL,例如.产品编号,名称,订单编号.每个函数都返回一个结构(例如,订单,产品)或错误:
I have written a simple package, which basically consists of a lot of getter functions. Each file in this package corresponds to a service, so for instance the product file, contains functions relating to the product service/db, order file to order service etc. Each function takes as parameters a db resource to the underlying db, and parameters for the sql, eg. productid, name, orderid. Each of the functions returns a struct (eg. Order, product) or an error:
// 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) {
...
}
问题是我无法从我的主程序包中模拟这些功能.我真正想做的是重写程序包,因此我可以以某种方式将函数传递给类型.但是我不确定该怎么做.特别是当函数采用不同的输入参数并返回不同的结构时,尤其如此.有办法吗?
The problem is that I'm not able to mock these functions from my main package. What I really like to do, is to rewrite the package, so I somehow can pass to function to a type instead. But I'm not sure how to do this. Especially not when the functions take different input parameters and return different structs. Is there a way to do this?
推荐答案
在Go中,您无法模拟函数声明,与对具体类型的方法声明相同,您无法模拟那些函数.
In Go you cannot mock a function declaration, same with method declarations on a concrete type, you cannot mock those.
例如:
func F()
func (T) M()
F
和M
在Go中不可模拟.
F
and M
are not mockable in Go.
但是,您可以模拟函数值,无论它们是变量,结构上的字段还是传递给其他函数的参数.
However you can mock function values, whether they are variables, fields on a struct, or parameters passed to other functions.
例如:
var Fn = func() { ... }
type S struct {
Fn func()
}
func F(Fn func())
在所有三种情况下,
Fn
都是可模拟的.
Fn
in all three cases is mockable.
您可以在Go中模拟的另一件事是interface
.大多数情况下,首选选项是interface
.
The other thing that you can mock in Go, and the prefered option most of the time, is an 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
类型的值.
Now any piece of code that depends on ProductRepository
can be passed a value of type ProductStore
when you're in "normal mode" and a value of type ProductRepositoryMock
when you're testing.
使用interface
s的另一个选项,它允许您保持主要的功能不变,即定义一个模仿*sql.DB
方法的接口,然后使用该接口类型作为要使用的类型.传递给您的函数,实现该接口的模拟版本,并在测试期间使用它.
Another option using interface
s, which allows you to keep your functions mostly unchaged is to define an interface that mimics the methods of *sql.DB
then use that interface type as the type to be passed to your functions, implement a mock version of that interface and use that during testing.
例如:
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) {
...
}
这篇关于如何在Golang中模拟函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!DB
参数现在是可模拟的.DB
parameter to GetProductByName
is now mockable.