duanba8070 2015-07-12 04:11
浏览 92
已采纳

在Go中模拟数据库/ sql结构

In testing database methods, I created a minimal wrapper over the database/sql package to allow me to test against interfaces instead of the difficult if not impossible to setup concrete classes. But, I get the following error when I try to mock sql.Stmt:

cannot use *sql.Stmt as type IStmt in return argument:
    *sql.Stmt does not implement IStmt (wrong type for Query method)
            have Query(...interface {}) (*sql.Rows, error)
            want Query(...interface {}) (IRows, error)

Here are my interfaces:

type IStmt interface {
    Query(args ...interface{}) (IRows, error)
    QueryRow(args ...interface{}) IRow
}

type IRows interface {
    Columns() ([]string, error)
    Next() bool
    Close() error
    Scan(dest ...interface{}) error
    Err() error
}

And here's the problem method:

func (c *DbConnection) Prepare(query string) (IStmt, error) {
    return c.conn.Prepare(query)
}

I know that one of the beautiful things about Go is that you can create your own interface and any struct that implements it will automatically "implement" it without having to use the implements keyword in java or use the semicolon like in C# to subclass. Why isn't it working with this return type? Am I doing something wrong?

  • 写回答

1条回答 默认 最新

  • 普通网友 2017-05-23 06:30
    关注

    Here's what I ended up creating to accomplish what I needed. Note that all of this is available in the onedb library that I created at: https://github.com/EndFirstCorp/onedb. In addition to mocking, onedb makes it possible to query Redis and OpenLDAP with the same methods.

    import "database/sql"
    
    type rowsScanner interface {
        Columns() ([]string, error)
        Next() bool
        Close() error
        Err() error
        scanner
    }
    
    type scanner interface {
        Scan(dest ...interface{}) error
    }
    
    type DBer interface {
        Ping() error
        Close() error
        Execute(query string, args ...interface{}) error
        Query(query string, args ...interface{}) (rowsScanner, error)
        QueryRow(query string, args ...interface{}) scanner
    }
    

    rowsScanner and scanner are essentially the interfaces for the return of the database/sql Query and QueryRow methods respectively. DBer is ultimately the interface I wish I could get out of database/sql so I could mock it. But, since I can't do that, I created an object which can do the conversion.

    type sqllibBackend struct {
        db *sql.DB
        DBer
    }
    

    sqllibBackend is the magic struct that does the conversion. It converts the output from *sql.DB methods into the mockable DBer interface. That just leaves the converter struct:

    func NewSqllib(driverName, connectionString string) (DBer, error) {
        sqlDb, err := sql.Open(driverName, connectionString)
        if err != nil {
            return nil, err
        }
        err = sqlDb.Ping()
        if err != nil {
            return nil, err
        }
        return &sqllibBackend{db: sqlDb}, nil
    }
    
    func (b *sqllibBackend) Close() error {
        return b.db.Close()
    }
    
    func (b *sqllibBackend) Query(query string, args ...interface{}) (rowsScanner, error) {
        return b.db.Query(query, args...)
    }
    
    func (b *sqllibBackend) QueryRow(query string, args ...interface{}) scanner {
        return b.db.QueryRow(query, args...)
    }
    
    func (b *sqllibBackend) Execute(query string, args ...interface{}) error {
        _, err := b.db.Exec(query, args...)
        return err
    }
    

    Now, rather than using the real database/sql, I can use sqllibBackend and it returns the easily mockable DBer interface.

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

报告相同问题?

悬赏问题

  • ¥15 NAO机器人的录音程序保存问题
  • ¥15 C#读写EXCEL文件,不同编译
  • ¥15 MapReduce结果输出到HBase,一直连接不上MySQL
  • ¥15 扩散模型sd.webui使用时报错“Nonetype”
  • ¥15 stm32流水灯+呼吸灯+外部中断按键
  • ¥15 将二维数组,按照假设的规定,如0/1/0 == "4",把对应列位置写成一个字符并打印输出该字符
  • ¥15 NX MCD仿真与博途通讯不了啥情况
  • ¥15 win11家庭中文版安装docker遇到Hyper-V启用失败解决办法整理
  • ¥15 gradio的web端页面格式不对的问题
  • ¥15 求大家看看Nonce如何配置