Forget mocking! Use real databases when testing with Docker Initiator.

Mocking away dependencies — especially external dependencies — is common practice when writing tests. Dependency injection typically makes it easy to provide a mock implementation for your dependencies, e.g. a database.

In this article we will discuss how to remove the need for mocking a SQL database.

Mock implementations almost always return static data which makes the test run whatever paths you think must be tested. It can be time consuming and difficult to predict all possible scenarios. Furthermore, an issue in the SQL query itself will never be caught in a mocked test. Even trivial syntax errors such as SELECT * FORM my_table will slip through.

You might be tempted to use some in-memory database in your tests. They’re fast, but will likely not match the exact SQL dialect you’re using. You will spend time finding quirks to get the queries, seeds and migrations to run in both dialects — even worse, you might be tempted to change your production code or run different code paths in test and production 😱.


Enter Docker Initiator

go-docker-initiator is an attempt at getting instances of services up and running quickly when you run your Go tests. As the name indicates it uses Docker to start up real instances of services the test depends upon. It waits for the instance to be ready and provides a simple programming interface to use. Currently it supports MySQL and Google PubSub Emulator out of the box.

It requires docker to be setup on the development environment.

Let’s look at some code. Assume a simple function which retrieves users from a database

A test for this, with a real MySQL server using go-docker-initiator would look like this

Note that if the whole UserRepository was mocked out, the code inside GetUsers would never have been run, losing testing on not only the SQL query, but also the logic for extracting the database result into the Go structs. If you mocked just the DB, you’d be able to test the logic for converting *sql.Rows to User structs, but the SQL query would remain untested.


This test takes about 10 seconds to run on my MacBook Pro 2017. This doesn’t matter much when running in CI environments but might be too slow if you’re running tests on each save. In this case it would be prudent to add // +build integration at the top of the file, in which case the tests will only be run with go test -tags integration my_test.go. Make sure this is included in your CI invocation if you opt for this route.