Integration Test (Database) in Golang using Dockertest

How we do the Integration Test in Golang using Dockertest

Bismo Baruno
Easyread

--

Photo by pixabay.com

Hi everyone! It’s me again, and I still want to share some experience, thought or opinions about technology-related to the software engineering field. Today, the topic that I will share with you is about my experience did Integration Test in Golang using Dockertest. This Integration Test will be focused on code that works with the database.

Before going further, what the definition of the Integration Test? In my opinion and based on my experience, if the Unit Test will be tested our codebase on mock data, then Integration Test will be focused on using the real data. Actually the real data is not only about the database, sometimes we could have external service for our app. But yeah, mostly our application will have a database, then in this article, we will be focused on Integration Test with the database.

How’s usually an Engineer solved this Integration Test? Based on my experiences, usually, they did this:

  • Setup real database test on VM or server
  • Setup a database test using a container like Docker

I think point 2 was simple enough for nowadays, but I realized that we could make it shorter in Go using Dockertest.

What is Dockertest? And Why?

Based on their project at https://github.com/ory/dockertest, actually Dockertest will have the same mechanism with point 2. But the Dockertest library provides easy to use commands for spinning up Docker containers and using them for your tests, as they said. Then, we don’t need to set up the docker manually by Dockerfile or docker-compose.

Still didn’t get any idea? Let’s try to write the test!

Dockertest supported multiple databases like MySQL, PostgreSQL, MongoDB, Redis, Cassandra, and etc. But, in this article, we will try using PostgreSQL. First, create the project structure like this:

+ your_gopath/
|
+--+ src/github.com/moemoe89
| |
| +--+ integration-test-golang/
| |
| +--+ main.go
| + repository/
| |
| +--+ repository.go
| |
| +--+ mysql
| | |
| | +--+ mysql.go
| | + mysql_test.go
| |
| +--+ postgres
| |
| +--+ postgres.go
| + postgres_test.go
|
+--+ bin/
| |
| +-- ... executable file
|
+--+ pkg/
|
+-- ... all dependency_library required

We will need to make 3 files: repository.go under repository directory and postgres.go and postgres_test.go under repository/postgres directory

repository.go just contain an interface for the implementation and struct for the data model.

Next, postgres.go will have the implementation of repository interface including migration and CRUD function. If you are already familiar with the clean architecture concept and dependency injection, that code is easy for you.

Last, the test file postgres_test.go will be testing the repository code using Dockertest. I will explain more on this part because we will use Dockertest code on this implementation.

First, we need to create a pool. Leave the string parameter empty if you want to use the default value.

pool, err := dockertest.NewPool("")
if err != nil {
log.Fatalf("Could not connect to docker: %s", err)
}

Second, we will start run the container here. That’s why we should define the options like image repository, tag, environment variables also exposed port.

opts := dockertest.RunOptions{
Repository: "postgres",
Tag: "12.3",
Env: []string{
"POSTGRES_USER=" + user,
"POSTGRES_PASSWORD=" + password,
"POSTGRES_DB=" + db,
},
ExposedPorts: []string{"5432"},
PortBindings: map[docker.Port][]docker.PortBinding{
"5432": {
{HostIP: "0.0.0.0", HostPort: port},
},
},
}
resource, err := pool.RunWithOptions(&opts)
if err != nil {
log.Fatalf("Could not start resource: %s", err.Error())
}

Next, we need to create a representation of the Repository interface for the Postgres db model that wrapped inside the pool retry. Why we need this mechanism? Because sometimes we need to wait for the Docker ready to serve the db connection.

if err = pool.Retry(func() error {
repo, err = postgres.NewRepository(dialect, dsn, idleConn, maxConn)
return err
}); err != nil {
log.Fatalf("Could not connect to docker: %s", err.Error())
}

The last step is purge. Purge will remove the docker container and linked volumes from docker.

if err := pool.Purge(resource); err != nil {
log.Fatalf("Could not purge resource: %s", err)
}

Okay! It’s time to run the test. Use go test ./... for executing the test case.

Picture 1 Integration Test Result

Nice, we have passed the test!! On picture 1 above, I also write for MySQL testing too. Because of that, when we check the docker images, it would be shown MySQL and Postgres images like in picture 2 below.

Picture 2 Docker Images

And the interesting part, we can do this on CI services like Travis CI. On my repository, you could see that I’m using Travis CI to execute the test!

This is the example .travis.yml file.

If you want to see the full example project for this test, you can visit my repository for this project on GitHub. Maybe I will add another database if I have time!

Hope you enjoy it, I’m happy if this article useful for you! Happy testing!

Thank you!

--

--