2018-03-16 18:35
浏览 105


I'm trying to create a unit test for the following function that is making database calls internally using Postgres driver:

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

And my test looks like this:

func (f *FakeDB) Query(query string, args ...interface{}) (*sql.Rows, error) {
    var rows *sql.Rows
    return rows, nil

However, after running this I get the following compilation error:

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)

When I look at the source code, sql.Rows does implement Close():


Any idea what I need to do here? Am I even taking the right approach to test List() here? I'm not particularly picky about testing parseRows() as it only contains calls to db.Rows, but I need to at least test List here.

  • 写回答
  • 好问题 提建议
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • doujiu3768 2018-03-16 18:46

    The problem is that DBer.Query returns a generic interface{}, which the compiler cannot assume has any methods at all:

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

    Meaning, unless you use a type assertion, you cannot call any methods on the returned value. Perhaps it should instead return (Rower, error)?

    The compiler, in effect, sees this:

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

    rows is a interface{}. It could be anything. It could be an int. It cannot assume it has any methods at all. So when you pass it to parseRows:

    parseRows(rows, appInfos)

    Defined as:

    func parseRows(rows Rower, infos []AppInfo) ([]AppInfo, error)

    Well, it takes a Rower, but you're passing an interface{}, which is not guaranteed to implement Rower. Hence the compiler error you're getting:

    interface {} does not implement Rower (missing Close method)

    It has nothing to do with the underlying value, but rather the type of the variable. The variable is of type interface{}, which has no methods, and therefor does not implement Rower.

    解决 无用
    打赏 举报

相关推荐 更多相似问题