While writing tests for a Go project I needed a way to test my database queries. For this the library sqlmock exists. This works great and also does what I wanted. However a problem arose that it seemed to share the connection across mocks even though they were re-init’d at the start of the test. This was due to me not implicitly calling a DSN for the sqlmock.

Before I had the following:

does not work as intended

// we only need the mock interface
_, mock, _ := sqlmock.New()

// open the database for gorm (*gorm.DB)
mockedGorm, _ := gorm.Open("sqlmock", "sqlmock_db_0")

// init your own database interface
dbInterface := NewDatabase(conf.Database)

// set the *gorm.DB on your interface
dbInterface.Conn = mockedGorm

// close the DB when the function is over
defer mockedGorm.Close()

This did work, however in running the second test, it complained the connection was closed. Weird because I called sqlmock.New() in each test. Turns out because you provide a DSN to Gorm you need to use the sqlmock.NewWithDSN("some_dsn_string") method.

does work

// we only need the mock interface
// explicitely set the DSN
_, mock, _ := sqlmock.NewWithDSN("sqlmock_db_0")

// open the database for gorm (*gorm.DB)
// ensure the DSN matches.
mockedGorm, _ := gorm.Open("sqlmock", "sqlmock_db_0")

// init your own database interface
dbInterface := NewDatabase(conf.Database)

// set the *gorm.DB on your interface
dbInterface.Conn = mockedGorm

// close the DB when the function is over
defer mockedGorm.Close()

What is important is that you do not share the DSN across your test functions as it’ll keep sharing the stubbed DB conn.

For easier tests and preventing to initiate this each time we can do the following:

var (
	dsn_count int64
)

func provideMock() (sqlmock.Sqlmock, *gorm.DB, *DispatchDB) {
	dsn := fmt.Sprintf("sqlmock_db_%d", dsnCount)
	_, mock, err := sqlmock.NewWithDSN(dsn)
	if err != nil {
		panic(err)
	}
	mockedGorm, err := gorm.Open("sqlmock", dsn)
	if err != nil {
		panic(err)
	}
	dbInterface := NewDatabase(conf.Database)
	dbInterface.Conn = mockedGorm
	dsnCount++

	return mock, mockedGorm, dbInterface
}

func TestMain(m *testing.M) {
	dsnCount = 0
	code := m.Run()
	os.Exit(code)
}

func Test_someFunc(t *testing.T) {
	mock, mockedGorm, dbInterface := provideMock()
	defer mockedGorm.Close()

	// your tests using the mock
}