Я выполнил этот учебник, чтобы имитировать соединение с базой данных. Но когда я пытаюсь пойти дальше и реализовать интерфейс для типа возвращаемого значения метода Query, я получаю сообщение об ошибке.
Вот рабочая версия, которая не работает для MockDB
, но работает для main
:
// The DB interface
type SQLDB interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
}
type MockDB struct {
// ...
}
func (m *MockDB) Query(query string, args ...interface{}) (SQLRows, error) {
// var row MockRows
// ...
return rows, nil
}
// #################################
// The Rows interface
type SQLRows interface {
Next() bool
Scan(dest ...interface{}) error
}
type MockRows struct {
// ...
}
func (m *MockRows) Next() bool {
// ...
return true
}
func (m *MockRows) Scan(dest ...interface{}) error {
// ...
return nil
}
// #################################
// Usage
func GetStuff(db SQLDB) (*sql.Rows, error) {
results := db.Query("query")
// ...
return results, nil
}
// #################################
// Main
var db SQLDB
func main() {
db = sql.Open("driver", "uri")
results := GetResult(db)
}
// #################################
// Test
func TestGetStuff(t *testing.T) {
mockDB = new(MockDB)
results := GetResult(mockDB)
// ...
}
Это работает для основного, но не для теста, и я получаю следующую ошибку при запуске тестов:
*MockDB does not implement SQLDB (wrong type for Query method)
have Query(string, ...interface {}) (SQLRows, error)
want Query(string, ...interface {}) (*sql.Rows, error)
Вот версия, которая работает для MockDB
, но не для main
:
// The DB interface
type SQLDB interface {
Query(query string, args ...interface{}) (SQLRows, error)
}
type MockDB struct {
// ...
}
func (m *MockDB) Query(query string, args ...interface{}) (SQLRows, error) { // <<<<<< Changed
// var row MockRows
// ...
return rows, nil
}
// #################################
// The Rows interface
type SQLRows interface {
Next() bool
Scan(dest ...interface{}) error
}
type MockRows struct {
// ...
}
func (m *MockRows) Next() bool {
// ...
return true
}
func (m *MockRows) Scan(dest ...interface{}) error {
// ...
return nil
}
// #################################
// Usage
func GetStuff(db SQLDB) (SQLRows, error) { // <<<<<< Changed
results := db.Query("query")
// ...
return results, nil
}
// #################################
// Main
var db SQLDB
func main() {
db = sql.Open("driver", "uri")
results := GetResult(db)
}
// #################################
// Test
func TestGetStuff(t *testing.T) {
mockDB = new(MockDB)
results := GetResult(mockDB)
// ...
}
И вот ошибка:
*sql.DB does not implement SQLDB (wrong type for Query method)
have Query(string, ...interface {}) (*sql.Rows, error)
want Query(string, ...interface {}) (SQLRows, error)
Как я могу заставить это работать для обоих? Или это просто невозможно сделать?
1 ответ
Что вы можете сделать, так это обернуть *sql.DB
тонкой оболочкой, которая реализует SQLDB
и чья реализация просто делегирует вызов обернутому *sql.DB
.
type dbwrapper struct{ db *sql.DB }
func (w *dbwrapper) Query(query string, args ...interface{}) (SQLRows, error) {
return w.db.Query(query, args...) // delegate
}
Теперь вместо прямого использования *sql.DB
вы оборачиваете его в dbwrapper
, который затем можно использовать как интерфейс SQLDB
.
func GetStuff(db SQLDB) (SQLRows, error) {
// ...
}
func main() {
db, err := sql.Open("driver", "uri")
if err != nil {
panic(err)
}
w := &dbwrapper{db}
rows, err := GetStuff(w) // will compile
// ...
}
Похожие вопросы
Связанные вопросы
Новые вопросы
go
Go — это язык программирования с открытым исходным кодом, с синтаксисом, частично унаследованным от C. Он имеет статическую типизацию с ограниченными возможностями динамической типизации; он также включает автоматическое управление памятью, встроенные примитивы параллелизма, массивы переменной длины, называемые слайсами, и большую стандартную библиотеку.
*sql.Rows
не является типом интерфейса.*sql.DB
какSQLDB
, потому что*sql.DB
не реализует интерфейсSQLDB
. Однако вы можете обернуть*sql.DB
в тонкую оболочку, которая реализуетSQLDB
и чья реализация просто делегирует вызов обернутому*sql.DB
.*sql.DB
тонкой оболочкой, которая реализует интерфейсSQLDB
, на самом деле это так просто.(*sql.Rows).Close()
ДОЛЖЕН быть вызван. как только вы закончите со строками, иначе вы потеряете соединения, и ваша программа в конечном итоге выйдет из строя. Поэтому вам нужно открыть его через интерфейсSQLRows
и вызвать вручную или убедиться, что он вызывается каким-то другим способом, каким бы он ни был.