I need to define these interfaces to mock official mongo driver
type MgCollection interface {
FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult
// Other methods
}
type MgDatabase interface {
Collection(name string, opts ...*options.CollectionOptions) MgCollection
// Other methods
}
In mongo driver package there are two structs mongo.Collection and mongo.Database with these methods
func (coll *Collection) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *SingleResult {
// Method code
}
func (db *Database) Collection(name string, opts ...*options.CollectionOptions) *Collection {
// Method code
}
The struct *mongo.Collection implement MgCollection correctly, so this code compiles without error
var col mgdriver.MgCollection
col = &mongo.Collection{}
col.FindOne(ctx, nil, nil)
But The struct *mongo.Database not implement MgDatabase, so I when I write something like this:
var db mgdriver.MgDatabase
db = &mongo.Database{}
db.Collection("Test", nil)
Compiler show this error:
cannot use &mongo.Database literal (type *mongo.Database) as type mgdriver.MgDatabase in assignment: *mongo.Database does not implement mgdriver.MgDatabase (wrong type for Collection method) have Collection(string, ...*options.CollectionOptions) *mongo.Collection want Collection(string, ...*options.CollectionOptions) mgdriver.MgCollection
Both mongo.Collection and mongo.Database are in the official package and I cannot change any code in that package. so how can I change my interfaces to mock official mongo driver correctly?
Usually, you do not. What you should do is to define a data access interface,
type CRUD interface {
Create(yourModel) error
Read(page, size, skip) []yourModel
Update(yourModel) error
Delete(yourModel) error
}
and implement it.
You then mock the interface, for example with testify/mock:
type MockedCRUD struct {
mock.Mock
}
func(m *MockedCRUD)Create(y yourModel) error{
returned m.Called(y).Error(0)
}
// And so on and so forth
Since MockedCRUD
satisfies the CRUD interface, you can use it as you would use your MongoCRUD
implementation, without any hassle:
func TestYourApplicationLogicCallingCreate( t *testing.T){
model := YourModel{Type: ”Baz”})
mocked := new(MockedCRUD)
mocked.On(”Create”,model).Return(ErrInvalidType)
app := YourApplication{CRUD:mocked}
err := app.yourApplicationLogicCallingCreate(model)
assert.Error(t,err)
assert.Equal(t,ErrInvalidType,err)
}
Remains the question how do you test the implementation of the CRUD interface. I used to use the mgo driver, originally developed by Gustavo Niemeyer and taken over by globalsign. That brought a nifty little package called dbtest. It is actually a very thin wrapper around a MongoDB instance, starting and stopping one on demand, with the ability to reset the data in between tests. Either just import dbtest
or, to quote a Go Proverb
A little bit of copy is better than a little bit of dependency.
(Remember to give credit, though, and retain the copyright notes.)
So, using the method described above you can do your unit tests pretty fast against a mock, with stable and predictable answers tailored to your tests and only do relatively expensive and slow tests against MongoDB only where you absolutely must.
Added bonus: it is relatively easy to swap out your actual persistence technology.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With