简体   繁体   English

如何在Golang中模拟函数

[英]How to mock functions in golang

I have written a simple package, which basically consists of a lot of getter functions. 我编写了一个简单的程序包,该程序包基本上包含许多getter函数。 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. 此软件包中的每个文件都对应一个服务,例如产品文件,包含与产品服务/数据库,订购文件到订购服务等相关的功能。每个功能都将基础数据库的db资源和参数用作参数。的SQL,例如。 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? 有没有办法做到这一点?

In Go you cannot mock a function declaration, same with method declarations on a concrete type, you cannot mock those. 在Go中,您不能模拟函数声明,与对具体类型的方法声明相同,也不能模拟那些函数声明。

For example: 例如:

func F()

func (T) M()

F and M are not mockable in Go. FM在Go中不可模拟。


However you can mock function values, whether they are variables, fields on a struct, or parameters passed to other functions. 但是,您可以模拟函数值,无论它们是变量,结构上的字段还是传递给其他函数的参数。

For example: 例如:

var Fn = func() { ... }

type S struct {
    Fn func()
}

func F(Fn func())

Fn in all three cases is mockable. 在所有这三种情况下, Fn都是可模拟的。


The other thing that you can mock in Go, and the prefered option most of the time, is an interface . 您可以在Go中模拟的另一件事是大多数情况下首选的选项,它是interface

For example: 例如:

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) {
    // ...
}

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. 现在,当您处于“正常模式”时,可以将依赖于ProductRepository任何代码段传递给ProductStore类型的值,而在进行测试时,可以传递给类型ProductRepositoryMock的值。


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. 使用interface s的另一种选择,它允许您使函数大部分保持不变:定义一个模仿*sql.DB方法的接口,然后使用该接口类型作为要传递给函数的类型,实现该接口的模拟版本界面,并在测试过程中使用它。

For example: 例如:

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 parameter to GetProductByName is now mockable. GetProductByName DB参数现在是可模拟的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM