我试图为使用Postgres驱动程序在内部进行数据库调用的以下函数创建单元测试:
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():
https://golang.org/pkg/database/sql/#Rows.Close
知道我需要做什么吗?我甚至采取正确的方法来测试List()吗?我对测试parseRows()不是特别挑剔,因为它只包含对db.Rows的调用,但我至少需要在这里测试List。
答案 0 :(得分:3)
问题是DBer.Query
返回一个通用interface{}
,编译器不能假设它有任何方法:
Query(query string, args ...interface{}) (interface{}, error)
意思是,除非使用类型断言,否则不能在返回值上调用任何方法。也许它应该返回(Rower, error)
?
编译器实际上看到了这一点:
rows, err := a.DB.Query(query)
rows
是interface{}
。它可能是任何东西。它可能是int
。它不能假设它有任何方法。所以当你把它传递给parseRows
时:
parseRows(rows, appInfos)
定义为:
func parseRows(rows Rower, infos []AppInfo) ([]AppInfo, error)
嗯,需要Rower
,但您传递的是interface{}
,但无法保证实施Rower
。因此,你得到的编译器错误:
interface {} does not implement Rower (missing Close method)
它与基础值无关,而是与变量的类型无关。变量属于interface{}
类型,没有方法,因此不实现Rower
。