Aco
@Aco
Заклинатель кода

Как организовать тесты в пакетах вида адаптер?

В приложении есть пакет repo который содержит несколько интерфейсов - там описаны все необходимые для приложения методы. Далее есть различные пакеты (адаптеры) реализующие `repo` для разных баз данных: repo/mongodb, repo/redis, repo/sqlite.

Структура на фс такая:
  • repo
  • repo/mongodb
  • repo/redis
  • repo/sqlite


Проблема в том что эти адаптеры имеют одни и те же методы, делают одно и тоже (но по своему), и тестирование для них будет тоже — одинаковое. Очень затратно к каждому адаптеру подходить с тестами индивидуально, там всё идентично получается: и множество подготовленных фикстур и куча ассертов на каждый реализованный метод. Различаются только инициализацией перед тестом (создание баз и прочее) и зачисткой после тестов (удаление что насоздавали). Если делать в лоб то по сути будет ctrl+c - ctrl+v, а потом ещё меинтейнить всё это...

Было бы удобно создать хелпер в repo, который тупо проверял все адаптеры по одному алгоритму, но тут проблема с цикличным импортом (все адаптеры импортируют компоненты из repo). Можно хелпер в отдельный пакет унести repo/internal, но тогда всё что относится к инициализацией перед тестом и зачисткой после тестов, в адаптерах, надо делать доступным для других пакетов. А там тогда потянется и testing в сборку (используется для ассертов в инициализации и зачистки).

Вот и думаю
1) как же так распихать что бы в адапетрах писать только инициалзацию перед тестом и зачистку после тестов?
2) как в одном месте прогонять тесты по всем адамптерам?
3) как оставить всё тестовое в *_test.go файлах и не мусорить хелперами для тестов в коде адаптеров?
4) как сделать что бы не тянуть пакет testing и прочее тестовое в конечную сборку?

Тестов уже и так много, каждый раз переиначивать - будет сложно. Может кто занимался организацией тестов для таких условий? Или есть ссылка на доку по такому случаю (гуглится всё мимо)?
  • Вопрос задан
  • 64 просмотра
Пригласить эксперта
Ответы на вопрос 2
EvgenyMamonov
@EvgenyMamonov Куратор тега Go
Senior software developer, system architect
У меня в некоторых проектах схема очень похожа на вашу.
Я выношу тесты и инициализацию в папку repo.
Структура папок выглядит таким образом (беру пакет users для примера)
users/repo.go - описание интерфейса Repo
users/repo/repo.go - тут инициализация любого типа реализации в зависимости от конфига
users/repo/repo_test.go - тут тесты
  (создаю все типы репозиториев (mongo, redis...)) и одними и теми же тестами прогоняю каждый тип. 
  Тут и инициализация репозиториев для тестов и зачистка после тестов.)
users/repo/mongodb - реализация (импортирует пакет users при необходимости)
users/repo/redis
users/repo/sqlite

В приложении (там, где идёт инициализация) импортируется только пакет "users/repo", он (на основе конфига) подключается базе нужного типа.
А все остальные пакеты (если кому нужен интерфейс users.Repo) - импортируют пакет users
Ответ написан
WinPooh32
@WinPooh32
Stack Overflow answer searching expert
Проблема в том что эти адаптеры имеют одни и те же методы, делают одно и тоже (но по своему),

Это заворачивается в интерфейс и тестируете по этому интерфейсу ваши конкретные реализации.
https://gobyexample.com/interfaces

пример repo_test.go:
type Repository interface{
    MethodA() error
    MethodB() error
}

func TestRedis(t *testing.T) {
     testRepo(t, NewRedis(...))
}

func TestMongo(t *testing.T) {
     testRepo(t, NewMongo(...))
}

func testRepo(t *testing.T, repo Repository){
    err := repo.MethodA()
    if err != nil {
        t.Errorf("methodA: %s", err)
    }

    err = repo.MethodB()
    if err != nil {
        t.Errorf("methodB: %s", err)
    }
}


... но тут проблема с цикличным импортом (все адаптеры импортируют компоненты из repo).

Но тут можно только посочувствовать.
Самое простой выход из ситуации - это отдельный пакет, в котором будут тестироваться интерфейсы по схеме, которую я выше описал.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы