Home > Enterprise >  Golang GORM DB mock
Golang GORM DB mock

Time:01-27

I have to mock test a service.to create new service i need to pass gorm.DB{} but every time i pass it and run the test i get nil pointer error(panic). please help on how to mock gorm.DB{} instance correctly for unit testing.

func NewService(db *gorm.DB) Service {
    return &service{
        repo:  newReactionRepo(db),
    }
}

making the mock call in the test like this :-

mockDB = &gorm.DB{}

package.NewService(mockDB)

getting this error

testing.tRunner.func1.2({0x1648e40, 0x21cdd60})
    C:/Program Files/Go/src/testing/testing.go:1396  0x24e
testing.tRunner.func1()
    C:/Program Files/Go/src/testing/testing.go:1399  0x39f
panic({0x1648e40, 0x21cdd60})
    C:/Program Files/Go/src/runtime/panic.go:884  0x212
gorm.io/gorm.(*DB).Session(0x21ef260, 0xc000861a50)
    C:/Users/acb/sdk/go1.17/pkg/mod/gorm.io/[email protected]/gorm.go:215  0x3b
gorm.io/gorm.(*DB).WithContext(...)

CodePudding user response:

You could initialize db, ie:

//...
 mockDB := initDb()
 package.NewService(mockDB)
//...

func initDb() *gorm.DB {
    dsn := "host=localhost user=myuser password=mypassword dbname=mydb port=5432 sslmode=disable"

    db, err := gorm.Open(postgres.Open(dsn))
    if err != nil {
        log.Fatal("couldn't connect to db")
    }
    return db
}

CodePudding user response:

Since the gorm.DB type is a struct, it makes unit testing with it directly a bit difficult. With the nil pointer error you are getting, you may need to modify your code to check for a nil value passed in so that it doesn't try and call methods on a nil pointer.

If you want to have unit tests, you could wrap your database operations in an interface, and then provide a mock implementation that does not use a real database. This can be done by creating an interface that defines the methods needed for interacting with the database, and then creating a struct that implements this interface for the actual database operations.

For example:

// Database is an interface that defines methods for interacting with a database.
type Database interface {
    Create(data interface{}) error
    GetByID(id int, result interface{}) error
}

// RealDatabase is a struct that implements the Database interface
// using a real GORM connection.
type RealDatabase struct {
    // db is a connection to the database
    db *gorm.DB
}

// Create saves the data in the database
func (rdb *RealDatabase) Create(data interface{}) error {
    return rdb.db.Create(data).Error
}

// GetByID retrieves the data by ID from the database
func (rdb *RealDatabase) GetByID(id int, result interface{}) error {
    return rdb.db.First(result, id).Error
}

// MockDatabase is a struct that implements the Database interface
// using a mock GORM connection.
type MockDatabase struct {
    // data is a map of ID to data used for mocking.
    data map[int]interface{}
}

// Create does not do anything but returns no errors
func (mdb *MockDatabase) Create(data interface{}) error {
    return nil
}

// GetByID returns the data for a given ID
func (mdb *MockDatabase) GetByID(id int, result interface{}) error {
    data, ok := mdb.data[id]
    if !ok {
        return fmt.Errorf("data not found for ID: %d", id)
    }
    result = data
    return nil
}

In this example, the RealDatabase struct uses GORM to interact with the database, while the MockDatabase struct uses a map to mimic the behavior of the real database. The Create method in the MockDatabase struct does nothing and returns no errors, and the GetByID method returns the data for a given ID. You can also add more functionality to the mock struct and the interface to mimic the behavior of the real database as needed.

Finally, you could also use the Gomock library, which provides tools to automatically generate mock versions of interfaces you provide to it.

  • Related