Unit Testing a GROM Application With a Real DB Server | by Che Dan | Jul, 2022

Unit testing with a DB server running in docker is a much better choice than Sqlmock, usually

0* QV5 qmBkgma4ab
Photo by Phyllis on Unsplash

About two years ago, I wrote an article discussing how to test a GORM application with Sqlmock.

Time flies, and I realize that most developers do not adopt this approach (at least on my team). Writing a test case using Sqlmock is too complex to get started.

The core issue is that you need to write up the whole SQL snippet manually, then compare it with the output from GORM. This workload is already much more than writing the codes to be tested. After all, the reason we use GORM is to avoid writing raw SQL manually.

We need to find a better way.

In general, testing DB application is hard. The main reason is the database server itself.

  • If the dev team shares a remote database server, then data conflict is inevitable.
  • If we create accounts for every developer on this database server, then unit tests should be run with different accounts. And someone needs to maintain these accounts.
  • If we demand every developer install and set up a database server on their local workstation, we increase the difficulty of setting up a development environment.

Seems there’s no answer is good enough. But if we could run the database server locally with docker and integrate the test case with docker, that would be perfect.

Image process below:

  1. Start a database server with docker before each test suite. open a GORM connection to this server
  2. Before every test case runs, clear data in the database and re-create tables if needed
  3. Run all test cases
  4. Stop the database server after test suite execution
sequence diagram for unit tests

In theory, we could control the docker daemon with a command line. But with the help of dockertest, this target could be achieved easily.

Here’s a step-by-step tutorial for unit testing a GORM application with a real database server running in docker.

we’ll re-use the example application in the previous article. You can find the source code on Github.

class diagram

We take Postgres as an example in this article, but it could work with any other database.

Setting up test suite

According to the sequence diagram, we do a series of preparations:

  • In BeforeSuite, we create an instance of *gorm.DB and a function to clean up docker resources. The function setupGormWithDocker will be explained later
  • In AfterSuite, we call cleanupDocker to release docker-related resources.
  • In BeforeEach, we drop the default schema and then re-create it to make sure the database is clean and ready before every test case run.

Below are the explanations for the core function setupGormWithDocker:

  • Create a docker resource pool with dockertest.NewPool, which is used to run the docker container
  • Specify image name, image version, and environment variable with dockertest.RunOptions
  • fnConfig is a function to control bootstrap policy. In this case, we need the docker container to be auto-removed after stop and never auto-restart
  • We use pool.RunWithOptions to run the container, then create the cleanup function fnCleanup.
  • Since it will take some time to start the container, we need to wait until the container is ready. Only after that, we can return the *gorm.DB instance. Here, pool.Retry comes to the rescue.pool.Retry will execute the parameter function repeatedly until the function returns nil(which means the container is ready)

Building test case

With the preparations in the test suite, we could get a usable instance of *gorm.DB, connecting to a local database server, and the database will be cleared before every test case execution.

  • In BeforeEach we create an instance of Repository for testing. Calling repo.Migrate to create tables automatically, then create sample blog data.
  • The test cases are pretty intuitive. We call the repo methods, and verify the expected result is returned. There’s no mock here since we’re using a real database server.
  • Compared to Sqlmock, there’s no need to write raw SQL manually, and we could finish the entire test case in less than a hundred lines of code.
  • dockertest will pull images if necessary, but without a download progress prompt. Our suggestion is to run docker pull postgres:14 before running the test case for the first time.
  • Running the test suite may take several seconds since we need to start up the Postgres database server. It may feel slower than most in-memory unit-test, but it is acceptable.
  • During pool.Retry, some connection errors will be output. If you don’t like the disturbance, pass &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)} to gorm.Open to close log
  • Your application may depend on some Postgres extension. In this case, you can replace the standard Postgres image with a custom one. For example, if you need postgres all, you could use this docker image
  • Testing the GORM application against a real database server has a huge advantage.
  • With the help of dockertest, a local docker container could work with golang unit testing seamlessly.
  • Unlike Sqlmock, all the DB logics are running on a real database server, there’s no need for any mock, and test cases are significantly simplified.
  • Is there any reason to stick with Sqlmock now?
  • For the complete source code, please visit its repository.


How to Unit Test a GORM Application With Sqlmock.

News Credit

%d bloggers like this: