如何在 golang 中模拟函数 [英] How to mock functions in golang

查看:17
本文介绍了如何在 golang 中模拟函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个简单的包,它基本上由很多getter函数组成.这个包中的每个文件都对应一个服务,例如产品文件,包含与产品服务/数据库相关的函数,订单文件到订单服务等.每个函数都将底层数据库的数据库资源作为参数,并将参数用于sql,例如.productid,名称,orderid.每个函数都返回一个结构(例如订单、产品)或错误:

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 can NOT mock a function, or a method declared on a concrete type.

例如:

func F()

func (T) M()

FM 在 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.

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.

另一个使用 interfaces 的选项,它允许您保持您的函数大部分不受影响,它是定义一个模仿 *sql.DB<方法的接口/code> 然后使用该接口类型作为要传递给您的函数的类型,实现该接口的模拟版本并在测试期间使用它.

Another option using interfaces, 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) {
   ...
}

GetProductByName

DB 参数现在可以模拟了.

DB parameter to GetProductByName is now mockable.

这篇关于如何在 golang 中模拟函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆