I
I
Ivan Shalganov2021-03-21 01:13:25
go
Ivan Shalganov, 2021-03-21 01:13:25

How to organize tests in adapter type packages?

The application has a repo package that contains several interfaces - all the methods necessary for the application are described there. Then there are various packages (adapters) that implement `repo` for different databases: repo/mongodb , repo/redis , repo/sqlite .

The fs structure is like this:

  • repos
  • repo/mongodb
  • repo/redis
  • repo/sqlite


The problem is that these adapters have the same methods, do the same thing (but in their own way), and testing for them will be the same - the same. It is very expensive to approach each adapter with tests individually, everything turns out identically there: a lot of prepared fixtures and a bunch of assertions for each implemented method. They differ only in initialization before the test (creation of databases, etc.) and cleanup after tests (removal of what was created). If done directly, it will essentially be ctrl+c - ctrl+v, and then maintain all this...

It would be convenient to create a helper in repo that stupidly checked all adapters using one algorithm, but there is a problem with cyclic imports (all adapters import components from repo). You can take the helper into a separate package repo / internal, but then everything related to initialization before the test and cleanup after the tests, in adapters, should be made available to other packages. And then testing will also be pulled into the assembly (used for assertions in initialization and stripping).

So I'm thinking
1) how can I cram it so that in adapters I write only initialization before the test and cleanup after the tests?
2) how to run tests on all adapters in one place?
3) how to leave everything test in *_test.go files and not litter with helpers for tests in adapter code?
4) how to make sure that the testing package and other test packages are not pulled into the final assembly?

There are already so many tests, it will be difficult to change each time. Who can was engaged in the organization of tests for such conditions? Or is there a link to the dock on this occasion (everything is googled by)?

Answer the question

In order to leave comments, you need to log in

2 answer(s)
E
Evgeny Mamonov, 2021-03-21
@EvgenyMamonov

I have in some projects the scheme is very similar to yours.
I move the tests and initialization to the repo folder.
The folder structure looks like this (I take the users package as an example)

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

In the application (where the initialization takes place) only the "users/repo" package is imported, it (based on the config) is connected to the database of the required type.
And all other packages (if anyone needs the users.Repo interface) - import the users package

W
WinPooh32, 2021-03-21
@WinPooh32

The problem is that these adapters have the same methods, do the same thing (but in their own way),

This is wrapped in an interface and you test your specific implementations against this interface.
https://gobyexample.com/interfaces
repo_test.go example:
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)
    }
}

... but there is a problem with cyclic imports (all adapters import components from repo).

But here one can only sympathize.
The easiest way out of the situation is a separate package in which interfaces will be tested according to the scheme that I described above.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question