Golang GORM DB mock - unit-testing

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/gorm#v1.24.2/gorm.go:215 +0x3b
gorm.io/gorm.(*DB).WithContext(...)

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
}

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.

You might try something using composition.
type DBInstance struct {
*gorm.DB
}
Pass an instance of DBInstance to NewService(db DBInstance) instead of *gorm.DB. You can mock the methods of *gorm.DB that are used when unit testing your code. Example:
func (DBInstance) Find(dest interface{}, conds ...interface{}) (tx *DB) {
// mock implementation of gorm.DB.Find()
}
When calling from actual code use initialize DB instance using the db instance:
dsn := "host=localhost user=myuser password=mypassword dbname=mydb port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn))
dbInstance := DBInstance{
&db,
}
When calling from unit test, pass an empty uninitialized *gorm.DB object and mock all the required methods.
dbInstance := DBInstance{
&gorm.DB{},
}

Related

How to unit test a component that uses third party library in golang?

I am new to golang and came from a java background.
Here is my problem today: How to unit test a component that uses a third-party library that doesn't provide an interface in Golang? Here is my concrete example:
I have a class that uses golang mongodb driver to implement some DB operations like below:
package mypackage
type myClientBeingTested struct {
client *mongo.Client
}
func (mc *myClientBeingTested) FindOne(filter interface{}) (*mongo.SingleResult, error) {
result := mc.client.FindOne(context.Background(), filter)
if result.Err() == mongo.ErrNoDocuments {
return nil, nil
} else {
return nil, Errors.New("My own error message")
}
return result, nil
}
Now I'd like to write some unit tests for this method and realized that it's impossible to mock a third party dependency that doesn't have an interface implementation. In the example above, mongo.Client is a struct type. After some researching and thinking, the only possible way seems to be like below:
package mypackage
type myClientBeingTested struct {
client *mongo.Client
}
var findOneFunc = func(client *mongo.Client, ctx context.Context, filter interface{}) (*mongo.SingleResult, error) {
return client.findOne(ctx, filter)
}
func (mc *myClientBeingTested) FindOne(filter interface{}) (*mongo.SingleResult, error) {
result := findOneFunc(mc.client, filter)
if result.Err() == mongo.ErrNoDocuments {
return nil, nil
} else {
return nil, Errors.New("My own error message")
}
return result, nil
}
Then in my unit test I can stub findOneFunc with my own stub like below
findOneFunc = func(client *mongo.Client, ctx context.Context, filter interface{}) (*mongo.SingleResult, error) {
// my own implementation
}
But this seems to be a hack. Is there any authentic/recommended way to handling situations like that? Appreciate your responses!
It should be possible to write your own interface for the methods that you need to use from a struct imported from a 3rd party library.
type MongoClient interface {
FindOne(context.Context, mongo.D) (*mongo.SingleResult, error)
}
type myClientBeingTested struct {
client MongoClient
}
// in tests
type mockMongoClient struct {
// implement MongoClient, pass in to myClientBeingTested
}
However for most apps it provides a better guarantee to run tests against a local or in memory database to verify that everything works end to end. If that becomes too slow it can make sense to mock at the business logic level instead of the database query level.
For example:
type User struct {}
type UserMgmt interface {
Login(email, pass string) (*User, error)
}
// for testing api or workflows
type MockUserMgmt struct {}
// for production app
type LiveUserMgmt struct {
client *mongo.Client
}
In the unit test it would look like:
// user_mgmt_test.go test code
userMgmt := &LiveUserMgmt{client: mongo.Connect("localhost")}
// test public library methods
In api or workflow tests it would look like:
userMgmt := &MockUserMgmt{}
// example pass to api routes
api := &RequestHandler{
UserMgmt: userMgmt,
}
EDIT:
I'm too new to comment on my post, but re your question about mocking the struct, you apply the same principle. If the mongo type is a struct, you can create an interface (even with the same name) and depend on the interface instead of directly depending on the struct. Then via the interface you can mock out the methods you need to.
// The mongo struct you depend on and need to mock
type mongo struct {
someState string
}
// The real world function you need to mock out
func (m *mongo) Foo() error {
// do stuff
return nil
}
// Construct an interface with a method that matches the signature you need to mock
type mockableMongoInterface interface {
Foo() error
}
Now depend on mockableMongoInterface instead of directly on mongo. You can still pass your third party mongo struct to sites where you need it, because go will understand the type via the interface.
This aligns with Adrian's comment on your question.

How to mock a package method in Go?

Let's say I have a package with the following code:
package paths
type FilePath struct {
PathA string
}
func (c FilePath) GetPathA() string {
if err := PathExists(PathA); err != nil {
return ""
}
return PathA + "foo"
}
func PathExists(p string) error {
// call os and file methods
return err
}
How do I mock out the PathExists dependency to test FilePath? Also, method PathExists is being used by a lot of other packages as well. (I am open to suggestions of refactoring this to make it test friendly, keeping the following pointers in mind)
I have come across a few different approaches but none of them seems intuitive or idiomatic to me.
Have a global variable PE := PathExists in the package; in GetPathA, call err := PE(PathA) and in the test overwrite PE with a mock method.
Issue: If test package is something like paths_test, I will have to export PE which allows clients of the package to overwrite it as well.
Make PathExists a field of FilePath and mock the field in test.
Issue: Clients when using the package, will have to initialize PathExists field, or I provide a constructor of the form NewFilePath(PathtA string) which initializes the fields for me. In the actual use case there are a lot of fields, hence this approach fails as well.
Use an interface and embed it within the struct. When client uses it initialize with the actual method and for test mock it.
type PathExistser interface{
PathExists(p string) error
}
type FilePath struct{
PathA string
PathExister
}
type Actual struct{}
func (a Actual) PathExists(p string) error {
return PathExists(p)
}
Issue: Client again needs to provide the right implementation of the interface.
I have learnt of few more approaches doing something simimlar to the above options, such as make the method PathExists an argument for GetPathA, etc. All have the same concerns. Basically, I don't want the users of this package to have to figure out what should be the right input parameter to make sure the struct works as expected. Neither do I want the users to overwrite the behaviour PathExists.
This seems like a very straightforward problem and I seem to be missing something very funamental about go testing or mocking. Any help would be appreciated, thanks.
Method names are just for example. In reality GetPathA or PathExists would be way more complex.
To address the issue from your 1. approach, you can use an internal package which you'll then be able to import in paths_test but clients of your package won't be.
package paths
import (
// ...
"<your_module_path>/internal/osutil"
)
func PathExists(p string) error {
return osutil.PathExists(p)
}
package osutil
var PathExists = func(p string) error {
// call os and file methods
return err
}
// Use a mutex to make sure that if you have
// multiple tests using mockPathExists and running
// in parallel you avoid the possiblity of a data race.
//
// NOTE that the mutex is only useful if *all* of your tests
// use MockPathExists. If only some do while others don't but
// still directly or indirectly cause the paths.PathExists
// function to be invoked then you still can run into a data
// race problem.
var mu sync.Mutex
func MockPathExists(mock func(p string) error) (unmock func()) {
mu.Lock()
original := PathExists
PathExists = mock
return func() {
PathExists = original
mu.Unlock()
}
}
package paths_test
import (
// ...
"<your_module_path>/internal/osutil"
)
func TestPathExists(t *testing.T) {
unmock := osutil.MockPathExists(myPathExistsMockImpl)
defer unmock()
// do your test
}

How to mock request.Request in golang

I'm using github.com/aws/aws-sdk-go/aws/request to get presigned URLs, which I need to upload files to s3 bucket in AWS. I'm writing the test currently, for that I need to mock func (r *Request) Presign(expire time.Duration). request.Request is a struct, not an interface, so i have no idea, how can I mock it.
This isn't directly answering your question, but it might remove the basis of the question altogether.
A neat thing in Go, is that you are able to easily isolate your dependencies using interfaces. If your code, the part that you need to test, is using Presign indirectly, it is trivial to test.
I.e. create an interface
type HigherLevelAws interface {
Upload(file string) error
}
and use this interface in your code, along with Upload. Then you can easily mock this using e.g. https://godoc.org/github.com/stretchr/testify/mock
The actual implementation, would look something like this
type ActualAwsImpl struct {
aws *aws.Client
}
func (a *ActualAwsImpl) Upload(file string) error {
aws.Presign...
}
This allows you to test the business part of your code, but of course, still leaves untested code in ActualAwsImpl. This untested code, however, may be guaranteed to work by virtue of unit and integration tests in the aws sdk itself.
Either way, in my organization, we test this using fake aws services run in docker (e.g. https://github.com/gliffy/fake-s3).
I had to Mock a Request for a S3 command. Code below:
req, result := r.s3.ListObjectsV2Request(&s3.ListObjectsV2Input{
Bucket: aws.String(configs.Settings.S3Bucket),
})
err := req.Send()
The Mock function had to return a Request Object
func (m *mockS3ProviderClient) ListObjectsV2Request(input *s3.ListObjectsV2Input) (req *request.Request, output *s3.ListObjectsV2Output) {
err := input.Validate()
req = &request.Request{
HTTPRequest: &http.Request{},
Error: err,
}
output = &s3.ListObjectsV2Output{
Contents: []*s3.Object{
{Key:aws.String("File1")},
{Key:aws.String("File2")},
},
}
return req, output
}
You can create an interface for the function directly, like this:
type presigner interface {
Presign(expire time.Duration) (string, error)
}
If you implement your logic in a separate function that takes a presigner as a parameter like this (called dependency injection):
func Upload(p presigner, files string) error {
// ...
res, err := p.Presign(someduration)
if err != nil {
return err
}
// and so on
}
Then it's easy to mock in your tests - just implement the presigner interface and have the Presign function return what you expect:
type presignerMock struct {}
func (p *presignerMock) Presign(d time.Duration) (string, error) {
return "yay", nil
}
To test different scenarios you can add fields to the presignerMock and return them in your implementation:
type presignerMock {
res string
err error
}
func (p *presignerMock) Presign(d time.Duration) (string, error) {
return p.res, p.err
}

How to properly test a handler that calls another function inside of it

I am looking to test the PostUser function that looks something like this (error handling omitted for simplicity):
func PostUser(env *Env, w http.ResponseWriter, req *http.Request) error {
decoder := json.NewDecoder(req.Body)
decoder.Decode(&user)
if len(user.Username) < 2 || len(user.Username) > 30 {
return StatusError{400, errors.New("usernames need to be more than 2 characters and less than 30 characters")}
}
emailRe := regexp.MustCompile(`^[a-z0-9._%+\-]+#[a-z0-9.\-]+\.[a-z]{2,4}$`)
if !emailRe.MatchString(user.Email) {
return StatusError{400, errors.New("invalid email address")}
}
if len(user.Password) < 8 {
return StatusError{400, errors.New("passwords need to be more at least 8 characters")}
}
hashedPassword,_ := bcrypt.GenerateFromPassword([]byte(user.Password), 12)
env.DB.InsertUser(user.Username, hashedPassword, user.Email) // need to mock this out
userData,_ := json.Marshal(user)
defer req.Body.Close()
w.Write(userData)
return nil
}
My env.go file looks like this:
type Env struct {
DB *db.DB
}
My db.go file looks like this:
type DB struct {
Session *mgo.Session
}
How do I mock the InsertUser call by my DB struct, so that I can unit test the PostUser?
To use mocks for testing you need to create an interface that your mock can implement. Naturally, the struct you're replacing the mock with also needs to implement all the methods of the interface so that they are freely interchangeable.
For example, you could have an interface:
type DBInterface interface {
InsertUser(string, string, string)
//all other methods on the DB struct here
}
Then your DB struct already implements all the methods of the interface. From there you can create a mock struct that also implements the interface.
type DBMock struct {}
func (dbm *DBMock) InsertUser(username, password, email string) {
//whatever mock functionality you want here
return
}
//all other methods also implemented.
Then you can alter env to have a pointer to a DBInterface instead of a DB. When you setup the env to be passed into the handler, in the production version use the DB struct and in testing use the DBMock struct.

Golang Mocking - problems with type collision

I'm mocking out a DataStore and it's Get/Set functionality. The trouble I'm having is: cannot use s (type *MockStore) as type *datastore.Storage in argument to EventHandler
This is caused by my EventHandler function needing to be passed a *datastore.Storage as an argument type. I want to Test (http test) EvenHandler() using the MockStore I've created instead of the real datastore. I'm using the golang testify mock package.
Some Code Examples
type MockStore struct{
mock.Mock
}
func (s *MockStore) Get() ...
func EventHandler(w http.ResponseWriter, r *http.Request, bucket *datastore.Storage){
//Does HTTP stuff and stores things in a data store
// Need to mock out the data store get/sets
}
// Later in my Tests
ms := MockStore
EventHandler(w,r,ms)
A few things:
Create an interface that will be implemented both by datastore.Storage and your mock store.
Use the above interface as the argument type in EventHandler (not a pointer to the interface).
Pass a pointer to your MockStore to EventHandler, as the Get method is defined for a pointer to the struct.
Your updated code should be something like the following:
type Store interface {
Get() (interface{}, bool) // change as needed
Set(interface{}) bool
}
type MockStore struct {
mock.Mock
}
func (s *MockStore) Get() ...
func EventHandler(w http.ResponseWriter, r *http.Request,bucket datastore.Storage){
//Does HTTP stuff and stores things in a data store
// Need to mock out the data store get/sets
}
// Later in my Tests
ms := &MockStore{}
EventHandler(w,r,ms)