我正在尝试为Go Flex SDK for Google Cloud Datastore周围的测试模拟创建一个包装器。我目前正在使用
成功运行localhost模拟器gcloud beta emulators datastore start --no-store-on-disk
在我的测试窗口的一个单独的终端中,我更愿意创建一个模拟数据库模拟器,它作为测试过程本身的一部分运行(没有exec
上面的内容),这样我就可以并行运行多个测试,每个都有自己的数据库模拟器。
我遇到了Google SDK没有实现我的界面的问题。
我的包装器包含以下代码:
package google
import (
"context"
"cloud.google.com/go/datastore"
)
type (
// Datastore is a wrapper for the Google Cloud Datastore Client.
Datastore datastore.Client
// Datastorer represents things that can operate like a datastore.Client.
Datastorer interface {
Delete(context.Context, *datastore.Key) error
Get(context.Context, *datastore.Key, interface{}) error
GetAll(context.Context, *datastore.Query, interface{}) ([]*datastore.Key, error)
Put(context.Context, *datastore.Key, interface{}) (*datastore.Key, error)
PutMulti(context.Context, []*datastore.Key, interface{}) ([]*datastore.Key, error)
RunInTransaction(context.Context, func(Transactioner) error, ...datastore.TransactionOption) (*datastore.Commit, error)
}
// Transactioner represents things that can operate like a datastore.Transaction.
Transactioner interface {
Commit() (*datastore.Commit, error)
Delete(*datastore.Key) error
DeleteMulti([]*datastore.Key) error
Get(*datastore.Key, interface{}) error
GetMulti([]*datastore.Key, interface{}) error
Put(*datastore.Key, interface{}) (*datastore.PendingKey, error)
PutMulti([]*datastore.Key, interface{}) ([]*datastore.PendingKey, error)
Rollback() error
}
)
// Delete deletes the entity for the given key.
func (d *Datastore) Delete(ctx context.Context, key *datastore.Key) error {
return (*datastore.Client)(d).Delete(ctx, key)
}
// Get retrieves the entity for the given key.
func (d *Datastore) Get(ctx context.Context, key *datastore.Key, dst interface{}) error {
return (*datastore.Client)(d).Get(ctx, key, dst)
}
// GetAll retrieves all entities for the given query.
func (d *Datastore) GetAll(ctx context.Context, q *datastore.Query, dst interface{}) ([]*datastore.Key, error) {
return (*datastore.Client)(d).GetAll(ctx, q, dst)
}
// Put stores an entity for the given key.
func (d *Datastore) Put(ctx context.Context, key *datastore.Key, src interface{}) (*datastore.Key, error) {
return (*datastore.Client)(d).Put(ctx, key, src)
}
// PutMulti is a batch version of Put.
func (d *Datastore) PutMulti(ctx context.Context, keys []*datastore.Key, src interface{}) ([]*datastore.Key, error) {
return (*datastore.Client)(d).PutMulti(ctx, keys, src)
}
// RunInTransaction runs the given function in a transaction.
func (d *Datastore) RunInTransaction(ctx context.Context, f func(tx Transactioner) error, opts ...datastore.TransactionOption) (*datastore.Commit, error) {
return (*datastore.Client)(d).RunInTransaction(ctx, func(t *datastore.Transaction) error {
return f(t)
}, opts...)
}
请注意,这些界面不会模拟完整的SDK。我只包括我在代码中调用的函数。我稍后会根据需要添加新的。
当我尝试将*datastore.Client
的实例用作Datastorer
时,我收到以下错误:
cannot use client (type *"cloud.google.com/go/datastore".Client) as type Datastorer in field value:
*"cloud.google.com/go/datastore".Client does not implement Datastorer (wrong type for RunInTransaction method)
have RunInTransaction(context.Context, func(*"cloud.google.com/go/datastore".Transaction) error, ..."cloud.google.com/go/datastore".TransactionOption) (*"cloud.google.com/go/datastore".Commit, error)
want RunInTransaction(context.Context, func(Transactioner) error, ..."cloud.google.com/go/datastore".TransactionOption) (*"cloud.google.com/go/datastore".Commit, error)
因为*datastore.Client
需要一个func(*datastore.Transaction) error
的函数,而我的界面需要一个func(Transactioner) error
。
有没有办法改变它以便编译?
如果我可以使它工作,我计划创建实现我的Datastorer
和Transactioner
接口的类型,并使用map来模拟真实的数据库。对于转换,为了测试我可以使用sync.Mutex
如果我需要它们,但由于每个测试都是一个线程并且将获得自己的数据库对象,我可能不需要锁定它们。
答案 0 :(得分:2)
我已经使用此代码编译了它:
package google
import (
"context"
"cloud.google.com/go/datastore"
)
type (
// Datastore is a wrapper for the Google Cloud Datastore Client.
Datastore struct {
*datastore.Client
}
// Datastorer represents things that can operate like a datastore.Client.
Datastorer interface {
Delete(context.Context, *datastore.Key) error
Get(context.Context, *datastore.Key, interface{}) error
GetAll(context.Context, interface{}, interface{}) ([]*datastore.Key, error)
Put(context.Context, *datastore.Key, interface{}) (*datastore.Key, error)
PutMulti(context.Context, []*datastore.Key, interface{}) ([]*datastore.Key, error)
RunInTransaction(context.Context, func(Transactioner) error, ...datastore.TransactionOption) (*datastore.Commit, error)
}
// Querier represents things that can operate like a datastore.Query.
Querier interface {
Filter(string, interface{}) Querier
}
// Transactioner represents things that can operate like a datastore.Transaction.
Transactioner interface {
Commit() (*datastore.Commit, error)
Delete(*datastore.Key) error
DeleteMulti([]*datastore.Key) error
Get(*datastore.Key, interface{}) error
GetMulti([]*datastore.Key, interface{}) error
Put(*datastore.Key, interface{}) (*datastore.PendingKey, error)
PutMulti([]*datastore.Key, interface{}) ([]*datastore.PendingKey, error)
Rollback() error
}
)
// Delete deletes the entity for the given key.
func (d *Datastore) Delete(ctx context.Context, key *datastore.Key) error {
return d.Client.Delete(ctx, key)
}
// Get retrieves the entity for the given key.
func (d *Datastore) Get(ctx context.Context, key *datastore.Key, dst interface{}) error {
return d.Client.Get(ctx, key, dst)
}
// GetAll retrieves all entities for the given query.
func (d *Datastore) GetAll(ctx context.Context, q interface{}, dst interface{}) ([]*datastore.Key, error) {
return d.Client.GetAll(ctx, q.(*datastore.Query), dst)
}
// Put stores an entity for the given key.
func (d *Datastore) Put(ctx context.Context, key *datastore.Key, src interface{}) (*datastore.Key, error) {
return d.Client.Put(ctx, key, src)
}
// PutMulti is a batch version of Put.
func (d *Datastore) PutMulti(ctx context.Context, keys []*datastore.Key, src interface{}) ([]*datastore.Key, error) {
return d.Client.PutMulti(ctx, keys, src)
}
// RunInTransaction runs the given function in a transaction.
func (d *Datastore) RunInTransaction(ctx context.Context, f func(tx Transactioner) error, opts ...datastore.TransactionOption) (*datastore.Commit, error) {
return d.Client.RunInTransaction(ctx, func(t *datastore.Transaction) error {
return f(t)
}, opts...)
}
我将DataStore
更改为包含struct
的{{1}},并添加了一个新界面datastore.Client
,其中包含我在Querier
中使用的功能。我还更新了datastore.Query
以接受GetAll
而不是interface{}
,然后将其声明为*datastore.Query
。我不能接受*datastore.Query
,因为我无法传递Querier
类型的变量,因为它们不满足*datastore.Query
界面(Querier
会返回Filter
而是一个Querier
)。
使用在单独进程中运行的仿真器的所有现有测试都将通过。
<强>更新强>:
我将*datastore.Query
更改为
Datastore
并在Datastore datastore.Client
附近添加了一个包装Query
:
datastore.Query
现在,Query datastore.Query
界面包含
Datastorer
并且GetAll(context.Context, Querier, interface{}) ([]*datastore.Key, error)
函数定义为
GetAll
和func (d *Datastore) GetAll(ctx context.Context, q Querier, dst interface{}) ([]*datastore.Key, error) {
return (*datastore.Client)(d).GetAll(ctx, (*datastore.Query)(q.(*Query)), dst)
}
定义为
Query.Filter
在调用代码时,我使用
func (q *Query) Filter(filterStr string, value interface{}) Querier {
return (*Query)((*datastore.Query)(q).Filter(filterStr, value))
}
这个编译并且所有测试都在通过。
答案 1 :(得分:0)
我知道很久以前就已经问过这个问题,但是如果万一仍然有人想知道如何模拟Google数据存储区客户端和事务,这是我如何使其工作的快照。
type Client interface {
Get(ctx context.Context, key *datastore.Key, dst interface{}) (err error)
NewTransaction(ctx context.Context, opts ...datastore.TransactionOption) (t Transaction, err error)
Put(ctx context.Context, key *datastore.Key, src interface{}) (*datastore.Key, error)
}
type Transaction interface {
Commit() (c *datastore.Commit, err error)
Rollback() (err error)
Get(key *datastore.Key, dst interface{}) (err error)
Put(key *datastore.Key, src interface{}) (*datastore.PendingKey, error)
Delete(key *datastore.Key) error
}
type Datastore struct {
*datastore.Client
}
func (d *Datastore) NewTransaction(ctx context.Context, opts ...datastore.TransactionOption) (t Transaction, err error) {
return d.Client.NewTransaction(ctx, opts...)
}
当然,如果您针对数据存储区使用其他方法,则取决于您实现这些方法。
在测试中,我现在可以模拟数据存储客户端和事务,如:
mockedClient := mock_gcloud.NewMockClient(ctrl)
mockedTransaction := mock_gcloud.NewMockTransaction(ctrl)
...
mockedClient.EXPECT().NewTransaction(context.Background()).Return(mockedTransaction, nil).Times(1)
等等。