2015-07-12 6 views
2

При тестировании методов базы данных я создал минимальную оболочку над пакетом database/sql, чтобы позволить мне протестировать интерфейс, а не сложную, если не невозможную, настройку конкретных классов. Но, я получаю следующее сообщение об ошибке при попытке макете sql.Stmt:Mocking database/sql structs в Go

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 или нас 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 {}) (* Строки, ошибка)' и '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 
} 

rowsScanner и scanner существу интерфейсы для возвращения базы данных/SQL Query и QueryRow методы соответственно. 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.

Смежные вопросы