首页 > 解决方案 > Mocking official MongoDb driver

问题描述

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?

标签: mongodbunit-testinggomocking

解决方案


通常,您不会。你应该做的是定义一个数据访问接口,

type CRUD interface {
  Create(yourModel) error
  Read(page, size, skip) []yourModel
  Update(yourModel) error
  Delete(yourModel) error
}

并实施。

然后模拟接口,例如使用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

由于MockedCRUD满足 CRUD 接口,你可以像使用你的MongoCRUD实现一样使用它,没有任何麻烦:

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)

}

仍然是如何测试 CRUD 接口的实现的问题。我曾经使用最初由 Gustavo Niemeyer 开发并由 globalsign 接管的 mgo 驱动程序。这带来了一个漂亮的小包,叫做dbtest。它实际上是一个非常薄的 MongoDB 实例包装器,可以按需启动和停止一个实例,并能够在测试之间重置数据。要么只是导入dbtest,要么引用一句 Go Proverb

一点点复制比一点点依赖要好。

(但请记住给予信用,并保留版权说明。)

因此,使用上述方法,您可以非常快速地针对模拟进行单元测试,并为您的测试量身定制稳定且可预测的答案,并且仅在绝对必须的情况下针对 MongoDB 进行相对昂贵且速度较慢的测试。

额外的好处:更换您的实际持久性技术相对容易。


推荐阅读