A copy-paste go SQL mock for GORM

September 8, 2023 0 By addshore

When it comes to writing robust and reliable tests for your Go applications, having a well-structured and efficient testing setup is crucial. One common challenge in testing Go applications is dealing with database interactions. To ensure that your code functions correctly, it’s essential to create a controlled environment for database operations during testing. In this blog post, we’ll explore how to create a mock GORM database for testing purposes, allowing you to isolate and verify your database interactions in a controlled manner.

Here is some copy-and-paste code (explained below) which should get you started.

import (
	"github.com/DATA-DOG/go-sqlmock"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

func NewMockGORM() (GORM *gorm.DB, close func()) {
	db, mock, err := sqlmock.New()
	if err != nil {
		panic(err)
	}

	// GORM always runs this query, so mock it for all tests
	mock.ExpectQuery("SELECT VERSION()").WillReturnRows(sqlmock.NewRows([]string{"version"}).AddRow("5.7.0"))

	GORM, err = gorm.Open(mysql.New(mysql.Config{Conn: db}), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
	if err != nil {
		panic(err)
	}

	return GORM, func() { db.Close() }
}Code language: PHP (php)

The code block above defines a NewMockGORM function that sets up a mock GORM database instance for testing. Let’s break down what this code does and how it can be a valuable addition to your testing toolkit.

Setting up a Mock GORM Database

In the provided code block, we begin by creating a mock SQL database using the sqlmock package. This mock database allows us to intercept and control database queries and responses during our tests. Any queries made to this mock database will be handled according to our expectations.

One of the common queries GORM performs is "SELECT VERSION()," which retrieves the database server’s version. To ensure consistency in our tests, we use mock.ExpectQuery to specify that when this query is executed, it should return a predefined result.

Next, we create a GORM database instance (GORM) that uses the mock database connection (db) rather than a real database connection. This allows us to test GORM’s behaviour without actually hitting a live database.

Finally, we return the GORM instance along with a closure function that will close the mock database connection when our tests are done. This ensures that resources are properly cleaned up after testing.

With this NewMockGORM function in your testing arsenal, you can confidently write tests for your GORM-based code, knowing that your database interactions are isolated, controlled, and consistent.

Expanding on this

In reality, if your tests are actually interacting with the GORM ORM you will want to also return the mock to change it during tests or pass in a bunch of things to apply to the mock during this creation stage.

In my current primary go project I only use this mock setup for testing gin routes which have no actual database interactions, however, the way my project is wired up and tested there is still the expectation of a correctly configured GORM db instance.

Most of my current testing makes use of copyist.

The copyist library automatically records low-level SQL calls made during your tests. It then generates recording files that can be used to play back those calls without connecting to the real SQL database. Run your tests again. This time, they’ll run much faster, because now they do not require a database connection.

https://github.com/cockroachdb/copyist

The truth

Really I just wanted to write a short blog post that included this code snippet for easy copy and pasting. I don’t normally write such details about code blocks stepping through each part as above, but I got Chat GPT to write this one for me and it seems to have done a fair job…

Sometimes the short posts and simple topics seem to be some of the most useful!

I hope at least 3 people find this at least mildly useful in the coming year đź‘Ť