Database 在Golang中伪造DB方法
我正在尝试为以下函数创建一个单元测试,该函数使用Postgres驱动程序在内部进行数据库调用:Database 在Golang中伪造DB方法,database,unit-testing,go,mocking,Database,Unit Testing,Go,Mocking,我正在尝试为以下函数创建一个单元测试,该函数使用Postgres驱动程序在内部进行数据库调用: type DBer interface { Exec(query string, args ...interface{}) (sql.Result, error) Query(query string, args ...interface{}) (interface{}, error) QueryRow(query string, args ...interface{}) *s
type DBer interface {
Exec(query string, args ...interface{}) (sql.Result, error)
Query(query string, args ...interface{}) (interface{}, error)
QueryRow(query string, args ...interface{}) *sql.Row
Prepare(query string) (*sql.Stmt, error)
}
type AppInfoCtrl struct {
DB DBer
}
type Rower interface {
Next() bool
Close() error
Scan(...interface{}) error
}
func parseRows(rows Rower, infos []AppInfo) ([]AppInfo, error) {
defer rows.Close()
for rows.Next() {
var i AppInfo
if err := rows.Scan(&i.Id, &i.Platform); err != nil {
return nil, err
}
infos = append(infos, i)
}
return infos, nil
}
func (a *AppInfoCtrl) List() ([]AppInfo, error) {
query := "select id, platform from appinfo where deleted = false"
rows, err := a.DB.Query(query)
if err != nil {
return nil, err
}
appInfos := []AppInfo{}
parseRows(rows, appInfos)
return appInfos, nil
}
我的测试是这样的:
func (f *FakeDB) Query(query string, args ...interface{}) (*sql.Rows, error) {
fmt.Println(query)
var rows *sql.Rows
return rows, nil
}
但是,运行此命令后,我出现以下编译错误:
appinfo/controller.go:68:11: cannot use rows (type interface {}) as type Rower in argument to parseRows:
interface {} does not implement Rower (missing Close method)
当我查看源代码时,sql.Rows确实实现了Close():
知道我需要在这里做什么吗?我在这里是否采取了正确的方法来测试List()?我对测试parseRows()并不特别挑剔,因为它只包含对db.Rows的调用,但我至少需要在这里测试List。问题是
DBer.Query
返回一个通用的接口{}
,编译器不能假设它有任何方法:
Query(query string, args ...interface{}) (interface{}, error)
这意味着,除非使用类型断言,否则不能对返回值调用任何方法。也许它应该返回(Rower,error)
实际上,编译器可以看到:
rows, err := a.DB.Query(query)
行
是一个接口{}
。它可能是任何东西。它可以是int
。它不能假设它有任何方法。因此,当您将其传递给parseRows
时:
parseRows(rows, appInfos)
定义为:
func parseRows(rows Rower, infos []AppInfo) ([]AppInfo, error)
好的,它需要一个Rower
,但是您正在传递一个接口{}
,它不能保证实现Rower
。因此,将出现以下编译器错误:
interface {} does not implement Rower (missing Close method)
它与基础值无关,而是与变量的类型有关。变量的类型为
接口{}
,没有方法,因此不实现行程序
,感谢您的快速响应。我将类型更改为Rower,但出现以下错误:*sql.DB未实现appinfo.DBer(查询方法的类型错误)have Query(string,…interface{})(*sql.Rows,error)want Query(string,…interface{})(appinfo.Rower,error),这就是我最终将返回类型更改为*sql.Rows的原因。但是,当我运行我的测试rows.Close()时,会遇到以下问题:panic:runtime error:invalid memory address或nil pointer dereference,我需要弄清楚如何创建一个假*sql.rows,在调用Close()时它不会死机,实际上,您必须模拟它,而不仅仅是返回指向实际类型的nil指针。sql.Rows是一个相当大的结构。我希望它是一个界面。我现在正绞尽脑汁想办法模仿它。你不必希望它是一个界面。只需创建所需的接口,并使用它代替您的类型中的sql.Rows
。这就是Go的duck类型接口的优点——实现不必知道接口。@mohi666其实很简单,看看这个:(固定链接)。使用此选项,Dber的模拟实现可以返回Rower的模拟实现,并且可以在任何时候使用适配器。