Pull to refresh

Comments 9

Код было сложно тестировать из-за зависимости от слоя доступа к данным. Последний сложно мокать в тестах. Можно замокать работу Kafka или Redis, а вот с транзакциями баз данных это уже проблематично.

В чем была сложность тестирования слоя представления данных? go-sqlmock кмк, вполне годный для этой цели пакет. Мне он показался несколько избыточным, реализовал для себя вот такой интерфейс, т.к. работаем с sqlx пакетом:

// Sqlable -- для реализации оберток базовых sql, sqlx функций ExecContext(), GetContext(), SelectContext() @see internal/tests/mock_data.go
type Sqlable interface {
	// ExecContext -- имитация sql.ExecContext()!  @see internal/tests/mock_data.go: возвращает поле .RetExec
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	// SelectContext -- имитация sqlx.SelectContext()! @see internal/tests/mock_data.go: возвращает поле .RetSelect
	SelectContext(ctx context.Context, dest any, query string, args ...interface{}) error
	// GetContext -- имитация sqlx.GetContext()! @see internal/tests/mock_data.go: возвращает поле .RetGet
	GetContext(ctx context.Context, dest any, query string, args ...interface{}) error
}

и тестирование слоя данных стало тривиальным даже для относительно сложных случаев, когда запрос на выборку далее мапится в структуры для бизнес логики.

Реализация проверяет: а) совпадение набора параметров тексту запроса; б) текст запроса его регекспу в тесте; в) тип возвращаемого результата и данные из блока SELECT, если там нет .* Возвращаемые наборы - слайсы, позволяют мокать больше одного запроса в тестируемой функции работы с набором.

Можно расширить на контроль корректности транзакций при желании, пока не было нужды в этом. Интерфейсный подход в GO тем и замечателен, что позволяет вытворять вот такие шутки. ;)

Владимир, спасибо за вопрос. Cложность как раз заключалась в тестировании отката транзакций. Также мы использовали mongodb, и тот mongodb golang client, который мы юзали, он не реализовывал sql/driver

Ну .. там же не так сложно накатать моккер для транзакций с откатом или приемом .. А для Монго писал подобный моккер для тех же самых целей. Там нет ничего сложного

// MongoCollectable is an object that implements FindOne and Find.
type MongoCollectable interface {
	Find(context.Context, interface{}, ...*options.FindOptions) (*mongo.Cursor, error)
}

ИМХО

Нужно с осторожностью подходить к юнитам на слое доступа к данным. Это лишнее время и кодовая база, которую нужно поддерживать. Все становится еще сложнее, если нужно сменить СУБД или библиотеку для работы с ней.

Изоляция бизнес-логики от слоя доступа к данным, конечно, не решает всех проблем, но позволяет сфокусироваться на тестировании этой самой бизнес-логики (в которой зарыт смысл разработки всего приложения). Ну и моки можно уже не писать ручками, а взять какой-нибудь minimock и генерить по интерфейсу репозиториев доступа к данным.

  1. Нет тесной связности бизнес-логики и инфраструктуры: если мы захотим перейти на другую базу, то это не составит труда, так как мы работаем с инфраструктурным слоем через интерфейсы.

  2. Нет тесной связности бизнес-логики и инфраструктуры: если мы захотим перейти на другую базу, то это не составит труда, так как мы работаем с инфраструктурным слоем через интерфейсы.

Два раза - это для закрепления материала? :)

Задублировалось :) Спасибо!

п.3 Увеличилась производительность за счет того, что несколько разработчиков могут работать на одном проекте без конфликтов и проблем с мерджами, разработчики не мешают друг другу.

Извечный вопрос. Когда относить общение с миром в delivery (у вас controllers), а когда в repository?

В controllers заносим то, что влетает в приложение (use case). В repository - исходящий трафик. Если говорить про потоки данных, то controllers это handlers -> use case, repository это use case -> adapter

То есть нет такой сущности как контроллер? Под контроллером понимается взаимодействие handlers -> use case ?

Sign up to leave a comment.