doushansu9012 2017-12-04 22:42
浏览 15
已采纳

如何在Golang中模拟函数

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?

  • 写回答

1条回答 默认 最新

  • doubo1871 2017-12-04 23:59
    关注

    In Go you cannot mock a function declaration, same with method declarations on a concrete type, you cannot mock those.

    For example:

    func F()
    
    func (T) M()
    

    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.

    For example:

    var Fn = func() { ... }
    
    type S struct {
        Fn func()
    }
    
    func F(Fn func())
    

    Fn in all three cases is mockable.


    The other thing that you can mock in Go, and the prefered option most of the time, is an 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.


    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.

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 数值计算均差系数编程
  • ¥15 redis-full-check比较 两个集群的数据出错
  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办
  • ¥15 kylin启动报错log4j类冲突
  • ¥15 超声波模块测距控制点灯,灯的闪烁很不稳定,经过调试发现测的距离偏大
  • ¥15 import arcpy出现importing _arcgisscripting 找不到相关程序
  • ¥15 onvif+openssl,vs2022编译openssl64
  • ¥15 iOS 自定义输入法-第三方输入法
  • ¥15 很想要一个很好的答案或提示