I am trying to write unit tests for a GORM repository method using gomock to mock database interactions. However, I’m running into issues with nil pointer dereference errors. Below are the relevant parts of my code:
Repository Code:
<code>package repositories
import (
"github.com/CAVAh/api-tech-challenge/src/core/domain/entities"
"github.com/CAVAh/api-tech-challenge/src/infra/db/database"
"github.com/CAVAh/api-tech-challenge/src/infra/db/models"
)
type CustomerRepository struct {
DB database.Database
}
func (r CustomerRepository) FindFirstByCpf(entity *entities.Customer) (*entities.Customer, error) {
var customer models.Customer
db := r.DB.Where("cpf = ?", entity.CPF)
err := db.First(&customer).Error
if err != nil {
return nil, err
}
result := customer.ToDomain()
return &result, nil
}
</code>
<code>package repositories
import (
"github.com/CAVAh/api-tech-challenge/src/core/domain/entities"
"github.com/CAVAh/api-tech-challenge/src/infra/db/database"
"github.com/CAVAh/api-tech-challenge/src/infra/db/models"
)
type CustomerRepository struct {
DB database.Database
}
func (r CustomerRepository) FindFirstByCpf(entity *entities.Customer) (*entities.Customer, error) {
var customer models.Customer
db := r.DB.Where("cpf = ?", entity.CPF)
err := db.First(&customer).Error
if err != nil {
return nil, err
}
result := customer.ToDomain()
return &result, nil
}
</code>
package repositories
import (
"github.com/CAVAh/api-tech-challenge/src/core/domain/entities"
"github.com/CAVAh/api-tech-challenge/src/infra/db/database"
"github.com/CAVAh/api-tech-challenge/src/infra/db/models"
)
type CustomerRepository struct {
DB database.Database
}
func (r CustomerRepository) FindFirstByCpf(entity *entities.Customer) (*entities.Customer, error) {
var customer models.Customer
db := r.DB.Where("cpf = ?", entity.CPF)
err := db.First(&customer).Error
if err != nil {
return nil, err
}
result := customer.ToDomain()
return &result, nil
}
Database Interface:
<code>package database
import (
"gorm.io/gorm"
)
type Database interface {
Create(data interface{}) error
Where(query interface{}, args ...interface{}) *gorm.DB
First(dest interface{}, conds ...interface{}) error
}
type RealDatabase struct {
db *gorm.DB
}
func (rdb *RealDatabase) Create(data interface{}) error {
return rdb.db.Create(data).Error
}
func (rdb *RealDatabase) Where(query interface{}, args ...interface{}) *gorm.DB {
return rdb.db.Where(query, args...)
}
func (rdb *RealDatabase) First(dest interface{}, conds ...interface{}) error {
return rdb.db.First(dest, conds...).Error
}
</code>
<code>package database
import (
"gorm.io/gorm"
)
type Database interface {
Create(data interface{}) error
Where(query interface{}, args ...interface{}) *gorm.DB
First(dest interface{}, conds ...interface{}) error
}
type RealDatabase struct {
db *gorm.DB
}
func (rdb *RealDatabase) Create(data interface{}) error {
return rdb.db.Create(data).Error
}
func (rdb *RealDatabase) Where(query interface{}, args ...interface{}) *gorm.DB {
return rdb.db.Where(query, args...)
}
func (rdb *RealDatabase) First(dest interface{}, conds ...interface{}) error {
return rdb.db.First(dest, conds...).Error
}
</code>
package database
import (
"gorm.io/gorm"
)
type Database interface {
Create(data interface{}) error
Where(query interface{}, args ...interface{}) *gorm.DB
First(dest interface{}, conds ...interface{}) error
}
type RealDatabase struct {
db *gorm.DB
}
func (rdb *RealDatabase) Create(data interface{}) error {
return rdb.db.Create(data).Error
}
func (rdb *RealDatabase) Where(query interface{}, args ...interface{}) *gorm.DB {
return rdb.db.Where(query, args...)
}
func (rdb *RealDatabase) First(dest interface{}, conds ...interface{}) error {
return rdb.db.First(dest, conds...).Error
}
Unit Test Code:
<code>package repositories
import (
"testing"
"github.com/CAVAh/api-tech-challenge/src/core/domain/entities"
"github.com/CAVAh/api-tech-challenge/src/infra/db/models"
gomock "go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
)
func TestFindFirstByCpf_Success(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockDB := mocks.NewMockDatabase(ctrl)
repo := CustomerRepository{DB: mockDB}
entity := &entities.Customer{CPF: "12345678901"}
customerModel := models.Customer{
Name: "John Doe",
CPF: "12345678901",
Email: "[email protected]",
}
// Mock for returning the correct instance of gorm.DB
mockGormDB := &gorm.DB{}
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockGormDB).Times(1)
mockDB.EXPECT().First(gomock.Any()).DoAndReturn(func(dest interface{}) *gorm.DB {
*dest.(*models.Customer) = customerModel
return &gorm.DB{}
}).Times(1)
result, err := repo.FindFirstByCpf(entity)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, "John Doe", result.Name)
assert.Equal(t, "12345678901", result.CPF)
assert.Equal(t, "[email protected]", result.Email)
}
</code>
<code>package repositories
import (
"testing"
"github.com/CAVAh/api-tech-challenge/src/core/domain/entities"
"github.com/CAVAh/api-tech-challenge/src/infra/db/models"
gomock "go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
)
func TestFindFirstByCpf_Success(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockDB := mocks.NewMockDatabase(ctrl)
repo := CustomerRepository{DB: mockDB}
entity := &entities.Customer{CPF: "12345678901"}
customerModel := models.Customer{
Name: "John Doe",
CPF: "12345678901",
Email: "[email protected]",
}
// Mock for returning the correct instance of gorm.DB
mockGormDB := &gorm.DB{}
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockGormDB).Times(1)
mockDB.EXPECT().First(gomock.Any()).DoAndReturn(func(dest interface{}) *gorm.DB {
*dest.(*models.Customer) = customerModel
return &gorm.DB{}
}).Times(1)
result, err := repo.FindFirstByCpf(entity)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, "John Doe", result.Name)
assert.Equal(t, "12345678901", result.CPF)
assert.Equal(t, "[email protected]", result.Email)
}
</code>
package repositories
import (
"testing"
"github.com/CAVAh/api-tech-challenge/src/core/domain/entities"
"github.com/CAVAh/api-tech-challenge/src/infra/db/models"
gomock "go.uber.org/mock/gomock"
"github.com/stretchr/testify/assert"
)
func TestFindFirstByCpf_Success(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockDB := mocks.NewMockDatabase(ctrl)
repo := CustomerRepository{DB: mockDB}
entity := &entities.Customer{CPF: "12345678901"}
customerModel := models.Customer{
Name: "John Doe",
CPF: "12345678901",
Email: "[email protected]",
}
// Mock for returning the correct instance of gorm.DB
mockGormDB := &gorm.DB{}
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockGormDB).Times(1)
mockDB.EXPECT().First(gomock.Any()).DoAndReturn(func(dest interface{}) *gorm.DB {
*dest.(*models.Customer) = customerModel
return &gorm.DB{}
}).Times(1)
result, err := repo.FindFirstByCpf(entity)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, "John Doe", result.Name)
assert.Equal(t, "12345678901", result.CPF)
assert.Equal(t, "[email protected]", result.Email)
}
Error Message:
<code>--- FAIL: TestFindFirstByCpf_Success (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x60 pc=0x5c06df]
goroutine 9 [running]:
testing.tRunner.func1.2({0x6a8860, 0x94d7c0})
/usr/local/go/src/testing/testing.go:1631 +0x24a
testing.tRunner.func1()
/usr/local/go/src/testing/testing.go:1634 +0x377
panic({0x6a8860?, 0x94d7c0?})
/usr/local/go/src/runtime/panic.go:770 +0x132
go.uber.org/mock/gomock.(*Controller).finish(0xc0001f5950, 0x0, {0x6a8860, 0x94d7c0})
/home/cava/go/pkg/mod/go.uber.org/[email protected]/gomock/controller.go:270 +0x25f
go.uber.org/mock/gomock.(*Controller).Finish(0xc0001f5950)
/home/cava/go/pkg/mod/go.uber.org/[email protected]/gomock/controller.go:243 +0x37
panic({0x6a8860?, 0x94d7c0?})
/usr/local/go/src/runtime/panic.go:770 +0x132
gorm.io/gorm.(*Statement).AddClause(0x0, {0x7837a0, 0xc0001abe70})
/home/cava/go/pkg/mod/gorm.io/[email protected]/statement.go:269 +0x7f
gorm.io/gorm.(*DB).Limit(0xc0001f59b0, 0x1)
/home/cava/go/pkg/mod/gorm.io/[email protected]/chainable_api.go:332 +0x85
gorm.io/gorm.(*DB).First(0xc0001abd80?, {0x6a2ec0, 0xc0001fe750}, {0x0, 0x0, 0x0})
/home/cava/go/pkg/mod/gorm.io/[email protected]/finisher_api.go:119 +0x4c
github.com/CAVAh/api-tech-challenge/src/infra/db/repositories.CustomerRepository.FindFirstByCpf({{0x783530?, 0xc0001abd80?}}, 0xc0000a4ee8)
/mnt/c/Users/Cava/Documents/code/customer-service/src/infra/db/repositories/customer_repository.go:40 +0x115
github.com/CAVAh/api-tech-challenge/src/infra/db/repositories.TestFindFirstByCpf_Success(0xc0000d5520)
/mnt/c/Users/Cava/Documents/code/customer-service/src/infra/db/repositories/customer_repository_test.go:101 +0x37a
testing.tRunner(0xc0000d5520, 0x7315f0)
/usr/local/go/src/testing/testing.go:1689 +0xfb
created by testing.(*T).Run in goroutine 1
/usr/local/go/src/testing/testing.go:1742 +0x390
FAIL github.com/CAVAh/api-tech-challenge/src/infra/db/repositories 0.008s
</code>
<code>--- FAIL: TestFindFirstByCpf_Success (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x60 pc=0x5c06df]
goroutine 9 [running]:
testing.tRunner.func1.2({0x6a8860, 0x94d7c0})
/usr/local/go/src/testing/testing.go:1631 +0x24a
testing.tRunner.func1()
/usr/local/go/src/testing/testing.go:1634 +0x377
panic({0x6a8860?, 0x94d7c0?})
/usr/local/go/src/runtime/panic.go:770 +0x132
go.uber.org/mock/gomock.(*Controller).finish(0xc0001f5950, 0x0, {0x6a8860, 0x94d7c0})
/home/cava/go/pkg/mod/go.uber.org/[email protected]/gomock/controller.go:270 +0x25f
go.uber.org/mock/gomock.(*Controller).Finish(0xc0001f5950)
/home/cava/go/pkg/mod/go.uber.org/[email protected]/gomock/controller.go:243 +0x37
panic({0x6a8860?, 0x94d7c0?})
/usr/local/go/src/runtime/panic.go:770 +0x132
gorm.io/gorm.(*Statement).AddClause(0x0, {0x7837a0, 0xc0001abe70})
/home/cava/go/pkg/mod/gorm.io/[email protected]/statement.go:269 +0x7f
gorm.io/gorm.(*DB).Limit(0xc0001f59b0, 0x1)
/home/cava/go/pkg/mod/gorm.io/[email protected]/chainable_api.go:332 +0x85
gorm.io/gorm.(*DB).First(0xc0001abd80?, {0x6a2ec0, 0xc0001fe750}, {0x0, 0x0, 0x0})
/home/cava/go/pkg/mod/gorm.io/[email protected]/finisher_api.go:119 +0x4c
github.com/CAVAh/api-tech-challenge/src/infra/db/repositories.CustomerRepository.FindFirstByCpf({{0x783530?, 0xc0001abd80?}}, 0xc0000a4ee8)
/mnt/c/Users/Cava/Documents/code/customer-service/src/infra/db/repositories/customer_repository.go:40 +0x115
github.com/CAVAh/api-tech-challenge/src/infra/db/repositories.TestFindFirstByCpf_Success(0xc0000d5520)
/mnt/c/Users/Cava/Documents/code/customer-service/src/infra/db/repositories/customer_repository_test.go:101 +0x37a
testing.tRunner(0xc0000d5520, 0x7315f0)
/usr/local/go/src/testing/testing.go:1689 +0xfb
created by testing.(*T).Run in goroutine 1
/usr/local/go/src/testing/testing.go:1742 +0x390
FAIL github.com/CAVAh/api-tech-challenge/src/infra/db/repositories 0.008s
</code>
--- FAIL: TestFindFirstByCpf_Success (0.00s)
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x60 pc=0x5c06df]
goroutine 9 [running]:
testing.tRunner.func1.2({0x6a8860, 0x94d7c0})
/usr/local/go/src/testing/testing.go:1631 +0x24a
testing.tRunner.func1()
/usr/local/go/src/testing/testing.go:1634 +0x377
panic({0x6a8860?, 0x94d7c0?})
/usr/local/go/src/runtime/panic.go:770 +0x132
go.uber.org/mock/gomock.(*Controller).finish(0xc0001f5950, 0x0, {0x6a8860, 0x94d7c0})
/home/cava/go/pkg/mod/go.uber.org/[email protected]/gomock/controller.go:270 +0x25f
go.uber.org/mock/gomock.(*Controller).Finish(0xc0001f5950)
/home/cava/go/pkg/mod/go.uber.org/[email protected]/gomock/controller.go:243 +0x37
panic({0x6a8860?, 0x94d7c0?})
/usr/local/go/src/runtime/panic.go:770 +0x132
gorm.io/gorm.(*Statement).AddClause(0x0, {0x7837a0, 0xc0001abe70})
/home/cava/go/pkg/mod/gorm.io/[email protected]/statement.go:269 +0x7f
gorm.io/gorm.(*DB).Limit(0xc0001f59b0, 0x1)
/home/cava/go/pkg/mod/gorm.io/[email protected]/chainable_api.go:332 +0x85
gorm.io/gorm.(*DB).First(0xc0001abd80?, {0x6a2ec0, 0xc0001fe750}, {0x0, 0x0, 0x0})
/home/cava/go/pkg/mod/gorm.io/[email protected]/finisher_api.go:119 +0x4c
github.com/CAVAh/api-tech-challenge/src/infra/db/repositories.CustomerRepository.FindFirstByCpf({{0x783530?, 0xc0001abd80?}}, 0xc0000a4ee8)
/mnt/c/Users/Cava/Documents/code/customer-service/src/infra/db/repositories/customer_repository.go:40 +0x115
github.com/CAVAh/api-tech-challenge/src/infra/db/repositories.TestFindFirstByCpf_Success(0xc0000d5520)
/mnt/c/Users/Cava/Documents/code/customer-service/src/infra/db/repositories/customer_repository_test.go:101 +0x37a
testing.tRunner(0xc0000d5520, 0x7315f0)
/usr/local/go/src/testing/testing.go:1689 +0xfb
created by testing.(*T).Run in goroutine 1
/usr/local/go/src/testing/testing.go:1742 +0x390
FAIL github.com/CAVAh/api-tech-challenge/src/infra/db/repositories 0.008s
I’ve tried several different approaches, like:
<code>mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(&gorm.DB{Error: nil}).Times(1)
// AND
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockDB).Times(1)
// AND
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockDB.Where("cpf = ?", entity.CPF))
</code>
<code>mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(&gorm.DB{Error: nil}).Times(1)
// AND
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockDB).Times(1)
// AND
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockDB.Where("cpf = ?", entity.CPF))
</code>
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(&gorm.DB{Error: nil}).Times(1)
// AND
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockDB).Times(1)
// AND
mockDB.EXPECT().Where("cpf = ?", entity.CPF).Return(mockDB.Where("cpf = ?", entity.CPF))
but I keep encountering this nil pointer dereference error. What am I doing wrong? How can I correctly mock the GORM database interactions to test this repository method?
Any help would be greatly appreciated!