How to mock Couchbase CRUD Operations - Golang

Hi,

I am currently using the following couchbase package in GO project.
“gopkg.in/couchbase/gocb.v1”

var (
cbConnStr = config.Viper.GetString(
“database.couchbase.connectionstring”)
cbBucket = config.Viper.GetString(
“database.couchbase.bucket”)
)

type CbRepository struct {
Cluster *gocb.Cluster
Bucket *gocb.Bucket
}

func NewCbRepository() *CbRepository {
logger.BootstrapLogger.Debug(“Entering Repository.NewCbRepository() …”)

cluster, err := gocb.Connect(cbConnStr)
if err != nil {
	logger.BootstrapLogger.Error(err)
	panic(err)
}
cluster.Authenticate(gocb.PasswordAuthenticator{
	Username: config.Viper.GetString("database.couchbase.username"),
	Password: config.Viper.GetString("database.couchbase.password"),
})

bucket, err := cluster.OpenBucket(cbBucket, "")
if err != nil {
	logger.BootstrapLogger.Error(err)
	panic(err)
}
return &CbRepository{
	Cluster: cluster,
	Bucket:  bucket,
}

}

func (r CbRepository) Insert(item entity.Favorite) error {
logger.Logger.Debug(“Entering CbRepository.Insert() …”)
_, err := r.Bucket.Insert(“DocumentName”, item, 0)
if err != nil {
logger.Logger.Error(err)
switch err {
/

* Include -case gocb.Err
**- to handle Couchbase error types here as required.
* Fallback to default if none match
*/
case gocb.ErrTimeout:
return entity.ErrDatabaseFailure
case gocb.ErrKeyExists:
return entity.ErrItemExists
default:
return entity.ErrDefault
}
}
return nil
}

This couchbase package is not interface driven, so how do I mock the database request?
r.Bucket.Insert() —
r.Bucket.Upsert() etc

Kindly share your thoughts.

My program structure looks like this:

  1. Main > Handler > Service layer > repositoryCb>repository

Note: Repository can be more than one, say couchbase or AWS Documentdb. That why I have separate Implementation for repositoryCb

Using mockGen,
I have mocked repository interface in service layer.
Mocked service layer in handler

My problem is how to mock the core CRUD operations like bucket.Insert(), bucket.Upsert(), bucket.Get() etc

Hi @Santhosh17713 maybe you could write your own interface that only has on it the functions that you need from the gocb bucket (and matches the function signatures). That way you could add a ConnectCbRepository(repository CbRepositoryInterface) function or similar and when you setup the repositoryCb in your tests you can pass ConnectCbRepository a mocked version instead of an actual cbRepository. Would something like that work?

Thanks @chvck for your suggestion. Yes, I did try that. But the problem is the couchbase crud instructions like insert/upsert etc are having a receiver with type *gocb.Bucket. I am not sure, how to mock this.

@Santhosh17713 I see what you mean. If you were to change the types on CbRepository so that it looked more like

type CbRepository struct {
Cluster Cluster
Bucket Bucket
}

Where Cluster and Bucket are your own interfaces where Bucket looked something like (but with all the function calls that you use)

type Bucket interface {
	Insert(key string, value interface{}, expiry uint32) (gocb.Cas, error)
	Upsert(key string, value interface{}, expiry uint32) (gocb.Cas, error)
}

then in your tests rather than calling NewCbRepository() to create the CbRepository you could just inject your mocked Cluster and Bucket objects via &CbRepository{Cluster:&myMockCluster{}, Bucket:&myMockBucket{}}. This is somewhat dependant on how your tests work and how you inject the repository into the service layer but if you did that then you’d be mocking gocb out in such a way that the receiver type on the calls wouldn’t matter because CbRepository wouldn’t even know if the Bucket belonged to gocb.

Maybe that approach could work better?