SQL-mock'и нужны, чтоб заменять собой подключения к настоящим БД в тестах. Действительно, очень неудобно держать сервер СУБД только для прогона тестов. Особенно если вы не пишете код работы с БД и она вам не нужна, или если вы — CI.
Используем SQL-mock'и
Для Golang существует достаточно популярная реализация go-sqlmock, которая позволяет симулировать поведение реальной БД на уровне sql/driver. Нужно только запрограммировать выдачу определенных результатов в ответ на определенные запросы, и mock готов. Вот как выглядит этот код:
mock.ExpectQuery(`
INSERT INTO users (id, login, password, is_super)
VALUES ($1, $2, $3, $4)
RETURNING created_at;`).WithArgs(
driver.Value(1),
driver.Value("user01"),
driver.Value("second-P"),
driver.Value(false),
).WillReturnRows(func() *sqlmock.Rows {
rr := sqlmock.NewRows([]string{"created_at"})
rr.AddRow(time.Unix(1580051896, 410421000))
return rr
}())
Но это двойная работа
Ведь сначала вы создаете структуры в БД и заливаете тестовые данные, а потом вынуждены повторить тоже самое в виде кода SQL-mock'а. И в дальнейшем нужно поддерживать этот код в соответствии с изменениями в БД. При этом возможно не точное повторение из-за человеческого фактора.
Kодогенератор может сделать за вас
Идея проста и реализована в opensource-проекте go-sqltest:
- автор тестов оформляет их в виде специальных функций:
func test<TESTNAME>(t *testing.T, conn *sql.DB) { // код теста, использующий подключение к БД conn }
- и запускает с настоящей СУБД через кодогенератор sqlmockgen. Например, так:
sqlmockgen -out=sql_test.go -db=postgresql://postgres:postgres@localhost:5432/test?sslmode=disable .
- кодогенератор sqlmockgen перехватывает запросы тестов к БД и записывает их с ответами в виде sql-mock'ов и стандартных тестовых функций:
func Test<TESTNAME>(t *testing.T) { // тест, использующий sql-mock вместо подключения к реальной БД }
- все остальные могут запускать тесты стандартным образом и без реальной БД. Например, так:
go test ./...
- Profit!
Больше подробностей в README.
Детали реализации кодогенератора sqlmockgen
Для перехвата трафика между тестами и БД реализован специальный proxy sql/driver.
Драйвер не только пишет код sql-mock'а, но и одновременно настраивает один экземпляр. Очень удобно видеть рядом реальный вызов метода и этот же вызов в виде строки. Это пригодится при апгрейде версии go-sqlmock и, может быть, для какого-нибудь сценария в будущем.
Запуск тестов через кодогенератор сделан не очень изящно — переименование исходных файлов и вызов go test
внутри, но другого способа я пока не нашел.
Заключение
Кодогенератор еще очень молодой и был использован только в одном проекте. Будет отлично, если он пригодится кому-нибудь еще.
И если отзывы помогут сделать его лучше.