2015-07-12 35 views
2

在測試數據庫方法時,我創建了一個包含數據庫/ sql包的最小包裝,以允許我測試接口而不是難以設置具體類。但是,我得到以下錯誤,當我嘗試模擬sql.Stmt:在Go中嘲笑數據庫/ sql結構

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) 

這裏是我的接口:

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 
} 

和這裏的問題方法:

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

我知道Go的優點之一就是可以創建自己的接口,任何實現它的結構都會自動「實現」它,而不必在java或us中使用implements關鍵字e像C#中的分號子類。爲什麼它不使用這種返回類型?難道我做錯了什麼?

+0

'* sql.Rows'沒有實現'IStmt',所以你不能返回它。更改界面以返回具體類型(最快修復)。我還建議閱讀[Effective Go](https://golang.org/doc/effective_go.html#interface-names)並查看你的界面命名(即'Queryer'或'Queryable')和/或閱讀https ://robots.thoughtbot.com/interface-with-your-database-in-go – elithrar

+0

但是* sql.Stmt實現了和我一樣的Query方法和QueryRow方法。 Query(args ... interface {})(* Rows,error)'和'QueryRow(args ... interface {})* Row'。當然,我使用接口來表示'* sql.Rows'和'* sql.Row',但我使用的方法簽名完全相同。如果我改變這些方法分別返回'* sql.Rows'和'* sql.Row',它就會起作用。如果Go無法處理返回類型的嵌套接口,那真是太遺憾了! –

回答

1

這就是我最終創造出來完成我所需要的。請注意,所有這些都可以在我創建的onedb庫中獲得:https://github.com/EndFirstCorp/onedb。除了模擬,onedb還可以用相同的方法查詢Redis和OpenLDAP。

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 
} 

rowsScannerscanner基本上數據庫/ SQL的分別QueryQueryRow方法的返回的接口。 DBer最終是我希望能從數據庫/ sql中獲得的接口,所以我可以嘲笑它。但是,由於我無法做到這一點,我創建了一個可以進行轉換的對象。

type sqllibBackend struct { 
    db *sql.DB 
    DBer 
} 

sqllibBackend是進行轉換的神奇結構。它將來自*sql.DB方法的輸出轉換爲可嘲諷的DBer接口。這只是離開轉換器的結構:

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 
} 

現在,而不是使用真實的數據庫/ SQL,我可以使用sqllibBackend它返回容易mockable DBER接口。