【问题标题】:Mocking official MongoDb driver模拟 MongoDb 官方驱动
【发布时间】:2019-09-17 04:59:59
【问题描述】:

我需要定义这些接口来模拟官方的mongo驱动

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
}

在 mongo 驱动程序包中有两个结构 mongo.Collectionmongo.Database 带有这些方法

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
}

结构体*mongo.Collection正确实现MgCollection,所以这段代码编译没有错误

var col mgdriver.MgCollection
col = &mongo.Collection{}
col.FindOne(ctx, nil, nil)

但是结构*mongo.Database没有实现MgDatabase,所以我写的时候是这样的:

var db mgdriver.MgDatabase
db = &mongo.Database{}
db.Collection("Test", nil)

编译器显示此错误:

不能使用 &mongo.Database literal (type *mongo.Database) 作为类型 分配中的 mgdriver.MgDatabase:*mongo.Database 未实现 mgdriver.MgDatabase(Collection 方法的类型错误)有 集合(字符串,...*options.CollectionOptions)*mongo.Collection 想要 Collection(string, ...*options.CollectionOptions) mgdriver.MgCollection

mongo.Collectionmongo.Database 都在官方包中,我无法更改该包中的任何代码。那么如何更改我的接口以正确模拟官方 mongo 驱动程序?

【问题讨论】:

    标签: mongodb unit-testing go mocking


    【解决方案1】:

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

    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 谚语

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

    (不过,请记得注明出处,并保留版权说明。)

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

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

    【讨论】:

    • 正如你提到的,我为 CRUD 定义了一个接口,所以我可以在上层模拟 CRUD 操作。但我的问题是关于为 CRUD 操作编写测试。我决定使用官方的mongo驱动,它不包含dbtest之类的东西。
    • @VahidJafari 您仍然可以在测试中使用import github.com/globalsign/mgo/dbtest。或者按照建议进行一些复制。
    【解决方案2】:

    如果有人仍然需要接口,我实现了一个简单的包装器来为所有主要类提供接口。请查看GitHub project

    它具有以下对象的接口:

    • 客户
    • 数据库
    • 会话
    • 更改流
    • 光标
    • 收藏
    • 单结果
    • 索引视图

    主要问题是它需要使用包装器而不是直接对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多