golang: создание интерфейсов для существующих типов, чтобы сделать код тестируемым

Я построил следующие 2 интерфейса, чтобы сделать свой код, который делает вызовы пакета sql, тестируемыми:

type Database interface {
    Close() error
    Query(string, ...interface{}) (DatabaseRows, error)
}

type DatabaseRows interface {
    Close() error
    Next() bool
    Scan(...interface{}) error
}

и фактический код, который я хочу протестировать:

func getDatabase(connectionString string) (db Database, err error) {
    if db , err = sql.Open("mysql", connectionString); err != nil {
        glog.V(0).Infof("Error %s", err)
    }
    return
}

Но это не компилируется:

* sql.DB не реализует базу данных (неправильный тип для метода запроса)

есть запрос (строка, ... интерфейс {}) (* sql.Rows, ошибка)

хотите Query (строка, ... интерфейс {}) (DatabaseRows, ошибка)

Если я правильно понимаю, он сообщает мне, что не может вернуть * Row там, где ожидается DatabaseRow, даже если структура Rows в реализует все 3 функции, которые я объявил в интерфейсе DatabaseRows.

Почему компилятор не создает эту ассоциацию?


go
person Amir Keibi    schedule 14.12.2016    source источник
comment
Да, *sql.Rows реализует DatabaseRows. Проблема в том, что сигнатуры методов различаются, что приводит к разным наборам методов.   -  person JimB    schedule 15.12.2016
comment
Я не слежу. Я скопировал подпись Query func из источника. Даже взглянув на сообщение об ошибке, можно было увидеть, что единственная разница между подписью 2 - это возвращаемое значение.   -  person Amir Keibi    schedule 15.12.2016
comment
Это единственная разница, но не может быть какой-либо разницы, или это другая подпись.   -  person JimB    schedule 15.12.2016
comment
Ага! Можно подумать, что это очевидно. Спасибо.   -  person Amir Keibi    schedule 15.12.2016


Ответы (1)


Если вы еще не нашли способ обойти это, я бы порекомендовал создать новый API для ваших вызовов БД, которые фактически возвращают то, что вы хотите. Это означает, что вам нужно будет применить определенное суждение, но ваши тесты станут чище, если вы немного поразмышляете над своей проблемой.

Например, если ваша БД содержит такую ​​таблицу

+------------+----------+-----+-------------------+
| EmployeeID | Name     | Age | Salary            |
+------------+----------+-----+-------------------+
| 123        | Kara Bob | 28  | 1 Million Dollars |
+ ...

Создайте такой интерфейс

type Employee interface {
    GetEmployeeId() int
    GetName() string
    ...
}

Затем создайте реальную версию, вызывающую вашу базу данных SQL, а затем поддельную версию с полями структуры для ваших тестов.

person Frank Bryce    schedule 01.09.2018
comment
Я создал интерфейсы так же, как упомянул в своем вопросе, но создал типы, которые реализуют эти интерфейсы и обернули фактические типы библиотеки базы данных / sql. Таким образом, я мог создавать (или использовать другие) типы тестов. - person Amir Keibi; 02.09.2018