在Golang中伪造数据库方法

时间:2018-03-16 18:35:09

标签: 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{}) *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。

1 个答案:

答案 0 :(得分:3)

问题是DBer.Query返回一个通用interface{},编译器不能假设它有任何方法:

Query(query string, args ...interface{}) (interface{}, error)

意思是,除非使用类型断言,否则不能在返回值上调用任何方法。也许它应该返回(Rower, error)

编译器实际上看到了这一点:

rows, err := a.DB.Query(query)

rowsinterface{}。它可能是任何东西。它可能是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