问题描述
我目前正在编写一个写入 QLDB 分类帐的应用程序。我有一个类似的功能:
// Use an interface that matches the QLDB Driver so we can inject / mock
type ILedgerDriver interface {
Execute(ctx context.Context,fn func(txn qldbdriver.Transaction) (interface{},error)) (interface{},error)
Shutdown(ctx context.Context)
}
// Create checks for a records existence before inserting on to the ledger.
func Create(driver ILedgerDriver,document interface{}) (interface{},error) {
return ls.Driver.Execute(context.Background(),func(txn qldbdriver.Transaction) (interface{},error) {
result,err := txn.Execute("SELECT * FROM People WHERE ID = ?",id)
if err != nil {
return nil,errors.Wrap(err,"error selecting document")
}
// Check if there are any results
if result.Next(txn) {
// document exists
return nil,nil
}
result,err = txn.Execute("INSERT INTO People ?",document)
if err != nil {
return nil,"error inserting document")
}
return result,nil
})
}
然后我试着用这样的东西来嘲笑:
// implements qldbdriver.Transaction.
type mockQldbTx struct{}
func (mockQldbTx) Execute(statement string,parameters ...interface{}) (*qldbdriver.Result,error) {
for _,p := range parameters {
if ps,_ := p.(string); ps == "ERROR" {
return nil,errors.New("execute Failed")
}
if ps,_ := p.(string); ps == "WILLFINDME" {
emptyResult := &qldbdriver.Result{}
return emptyResult,nil
}
}
return nil,nil
}
func (mockQldbTx) BufferResult(result *qldbdriver.Result) (*qldbdriver.BufferedResult,error) {
return nil,nil
}
func (mockQldbTx) Abort() error {
return nil
}
// implements ILedgerDriver
type mockDriver struct{}
func (mockDriver) Execute(ctx context.Context,error) {
mockTx1 := mockQldbTx{}
result,err := fn(mockTx1)
return result,err
}
func (mockDriver) Shutdown(ctx context.Context) {
}
这在很大程度上是有效的。但是,由于 qldbdriver.Result
不是接口,因此当结果具有 index
和 pageValues
属性(这会触发 {{ 1}} 块)。
有人对此有任何经验或可以指点任何指南吗?或者我真的只是有点过于谨慎,我真的不需要测试我的 create 函数是否有效? (以及除此之外的任何其他更大的业务逻辑将在另一个可以单独测试的函数中?)
解决方法
在对 QLDB 驱动程序的最新提交中,QLDB 团队引入了 Result
接口来解决您遇到的问题。请参阅解析 this commit 的 this issue。
复制该问题中的指南:以下是一个简化的代码片段,显示了对传递给 Execute
的函数的测试。 Transaction
和 Result
现在是接口,mockTransaction
和 mockResult
在我们的测试场景中实现。
// Your upsert method that can be passed into QLDBDriver.Execute()
func upsert(txn Transaction) (interface{},error) {
res,_ := txn.Execute("SELECT * FROM Table WHERE ID = '12345'")
if res.Next(txn) {
txn.Execute("UPDATE Table SET Name = 'Foo' WHERE ID = '12345'")
} else {
txn.Execute("INSERT INTO Person <<{'Name' : 'Foo','ID' : '12345'}>>")
}
return nil,nil
}
type mockTransaction struct {
mock.Mock
}
func (m *mockTransaction) Execute(statement string,parameters ...interface{}) (Result,error) {
args := m.Called(statement,parameters)
return args.Get(0).(Result),args.Error(1)
}
func (m *mockTransaction) BufferResult(res Result) (BufferedResult,error) {
panic("not used")
}
func (m *mockTransaction) Abort() error {
panic("not used")
}
type mockResult struct {
mock.Mock
}
func (r *mockResult) Next(txn Transaction) bool {
args := r.Called(txn)
return args.Get(0).(bool)
}
func (r *mockResult) GetCurrentData() []byte {
panic("not used")
}
func (r *mockResult) GetConsumedIOs() *IOUsage {
panic("not used")
}
func (r *mockResult) GetTimingInformation() *TimingInformation {
panic("not used")
}
func (r *mockResult) Err() error {
panic("not used")
}
func TestUpsert(t *testing.T) {
// Document already exists (update)
mockTxn := new(mockTransaction)
mockRes := new(mockResult)
mockRes.On("Next",mockTxn).Return(true).Once()
mockTxn.On("Execute","SELECT * FROM Table WHERE ID = '12345'",mock.Anything).Return(mockRes,nil)
mockTxn.On("Execute","UPDATE Table SET Name = 'Foo' WHERE ID = '12345'",nil)
upsert(mockTxn)
mockTxn.AssertCalled(t,"Execute",mock.Anything)
mockTxn.AssertNotCalled(t,"INSERT INTO Person <<{'Name' : 'Foo','ID' : '12345'}>>",mock.Anything)
// Document did not exist (insert)
mockRes.On("Next",mockTxn).Return(false).Once()
mockTxn.On("Execute",mock.Anything)
}
这尚未发布,因此如果您能够针对最新代码进行构建,那就太好了,否则我们(我为 QLDB 工作)可以组织正式发布。