Go. FakeDb. Эмуляция работы БД в тестах

На днях писал тесты для модуля, который взаимодействует с базой данных. Привязывать модуль тестов к настоящей базе данных не хотелось — это создаёт дополнительные требования к окружению, где будет выполняться тестирование. Создавать сразу экземпляры типа sql.Rows, с нужными табличками также не хотелось — для меня этот тип "чёрный ящик", и хотелось бы, чтобы так оно и оставалось. Поиском по теме ничего интересного также не нашлось.
Захотел поделиться с сообществом своей находкой: в основной поставке Go, есть готовый инструмент для таких нужд: называется FakeDb.

Что умеет данный инструмент?

По сути это простейшая, но полноценная как объект DB, база данных, умеющая создавать таблицы, наполнять их и делать из них простейшие выборки. Все созданные таблицы временные и существуют, пока существует экземпляр FakeDb. Кроме того, доступна эмуляция задержки при вызове команды и эмуляция исключения.

Команды FakeDb

Команды состоят из фраз, разделённых вертикальной чертой — |. Фраза — это или ключевое слово, или выражение вида "ключ=значение". Где ключ — это имя столбца.

WIPE
Команда уничтожает таблицы и возвращает пустой результат. Кроме того это проверка, что нет взаимоблокировки.
Пример вызова:
WIPE

CREATE
Команда создаёт таблицу с указанными столбцами указанного типа.
Формат вызова:
CREATE|Имя_таблицы|Столбец1=Тип,...,СтолбецN=Тип

Поддерживаемые типы:

  • bool
  • nullbool — булево или NULL
  • int32
  • string
  • nullstring — строка или NULL
  • int64
  • nullint64 — int64 или NULL
  • float64
  • nullfloat64 — float64 или NULL
  • datetime
  • any — пустой интерфейс, т. е. любой тип

Пример вызова:
CREATE|people|name=string,age=int32,photo=any,dead=bool,bdate=datetime

INSERT
Команда добавляет строки в созданную таблицу.
Формат вызова:
INSERT|Имя_таблицы|Столбец1=Значение,...,СтолбецN
Здесь и далее значение можно задать тремя способами:
1) Явно указать значение, например 123.
2) Указать "?", а значение передать параметром.
3) Указать "? имя_параметра" и значение передать именованным параметром.

Пример вызова:
INSERT|people|name=Alice,age=?,photo=?photo

SELECT
Команда позволяет выбрать или все строки таблицы, или с некоторым отбором.
Формат вызова:

SELECT|Имя_таблицы|Столбец1,...,СтолбецN| SELECT|Имя_таблицы|Столбец1,...,СтолбецN|ОтборПоСтолбцу1=?,...,ОтборПоСтолбцуM

Пример вызова:

SELECT|categories|category_id,category_parent_id,category_name| SELECT|categories|category_id,category_parent_id,category_name|category_parent_id=?parent

В результате возвращается полновесный экземпляр sql.Rows.

WAIT
Имитирует задержку при выполнении команды. Необходимо добавить перед любой указанной выше командой.
Формат вызова:
WAIT|Задержка|Команда

Задержка указывается как количество и постфикс, обозначающий единицу измерения: s — секунды, n — наносекунды, u — миллисекунды, h — часы, и так далее (используется функция time.ParseDuration()).

Пример вызова:
WAIT|1s|SELECT|categories|category_id,category_parent_id,category_name|

PANIC
Генерирует исключение при вызове команды.
Формат вызова:
PANIC|Имя_метода|Команда

Значение "Имя_метода" будет помещено в поле stmt.panic (тип fakeStmt).

Пример вызова:
PANIC|blablabla|SELECT|categories|category_id,category_parent_id,category_name|

Использование

FakeDb создавался для тестирования пакета "sql" основной поставки в виде скрипта тестирования. В виде отдельного пакета я его не нашёл. Поэтому выделил в пакет сам и разместил здесь: gihub.
Для выделения в пакет потребовались незначительные правки.

Пример использования

package packname  import (     "fmt"     "testing"     "io/ioutil"     "database/sql"     "github.com/a1div0/fakedb" )      func TestFoo(t *testing.T) {      fc := &fakedb.FakeConnector{ }     db := sql.OpenDB(fc)     if db.Driver() != fakedb.Fdriver {         t.Error("OpenDB should return the driver of the Connector")         return     }     if _, err := db.Exec("WIPE"); err != nil {         t.Error("exec wipe: %v", err)     }     defer db.Close()      db.Exec("CREATE|users|user_email=string,user_id=int64")     db.Exec("INSERT|users|user_email=?,user_id=?", "test@email.com", 345)      rows, err := db.Query(         "SELECT|users|user_id|user_email=?email",         sql.Named("email", user_email),     )     if err != nil {         t.Error(err)     }      result, err := Foo(rows) // тестируемая функция     if err != nil {         t.Error(err)     }     if result != 123 {         t.Error("Торжественно сообщаем, что функция Foo() не работает ;)")     } }

Спасибо за внимание.

P. S.: Пожалуйста напишите в комментариях — используете ли вы в тестах эмуляцию БД? Если да — то какой пакет?

ссылка на оригинал статьи https://habr.com/ru/post/485280/

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *