Integration tests in Golang with dockertest
Do integration testing (or system testing) usually means to have a database populated with data, services like redis, elasticsearch, etc… working, In general, any infrastructure with which our software interacts.
The most common way to do it is to have a replica of our production infrastructure. Actually, it’s relatively easy to achieve using containers, for example, docker containers.
We can set up and run a container for every service we need to replicate, we can orchestrate it with docker-compose and create some makefiles or just a simple script to prepare the infrastructure and run the integration tests.
If your tests are independent (they should), you must find the way to “restart” the infrastructure services between tests, and this can be hard to get with a separated infrastructure setup and tests (the infra is set up in a script and the tests are in Go files)
If you are using Golang, you can use dockertest, a library with which you can manage and orchestrate the containers in your Go test files.
Manage the test infrastructure container from the Go files allow us to control which service we need in each test (for example, some package is using a database but not Redis, makes no sense to run the Redis for this test)
To install dockertest, just run
go get -u github.com/ory/dockertest/v3
The simplest way to set up the infrastructure with dockertest is to add the setup code in the
TestMain function in your test file.
TestMain is a function is called before running the tests in the package More info
This is an example of how to set up a MySQL service using dockertest
Now we have the database service working, but this database is empty. dockertest is using a generic MySQL image for the container and nothing related to our app is there.
If you follow my posts, you would remember I wrote a post about database migrations (if not you can take a look at it). In that post I talked about go-migrate a tool to run database migrations but, in it, I focused on the usage as CLI tool, now we will use it in our Go code
In the previous code in the line where we wrote
// RESERVED FOR DATABASE MIGRATIONS we will add this code
m, err := migrate.NewWithDatabaseInstance("file://<path-to-migration-folder>, "mysql", driver)
Then after dockertest ups the database, the migration tool populates the database and our integration tests can run with the same data in the database.
If the app has more than one package (that is the common situation), I put the services’ setup code in an independent file which is called from every package:
Then in each package’s test we only need to add
Doing it in that way on every test block the service runs in a new container making the test completely independent.
As a last tip, I recommend putting the integration test in a different package to avoid circular imports.