我正在尝试创建一个包装器,用于围绕 Go Flex SDK for Google Cloud Datastore 进行测试模拟。虽然我目前成功运行本地主机模拟器使用

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...)
}

请注意,这些接口(interface)不会模拟完整的 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

有什么办法可以改变它以便编译吗?

如果我可以让它工作,我计划创建实现我的 DatastorerTransactioner 接口(interface)的类型,并使用映射来模拟真实的数据库。就事务处理而言,如果需要,我可以使用 sync.Mutex 进行测试,但由于每个测试都是一个线程并且将获得自己的数据库对象,因此我可能不需要锁定它们。

最佳答案

我已经使用以下代码编译它:

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 更改为包含 structdatastore.Client 并添加了一个新接口(interface) Querier ,其中包含我从 datastore.Query 使用的函数。我还更新了 GetAll 以接受 interface{} 而不是 *datastore.Query ,然后将其类型断言为 *datastore.Query 。我不能让它接受 Querier 因为这样我就不能传递 *datastore.Query 类型的变量,因为它们不满足 Querier 接口(interface)( Filter 返回一个 Querier 而不是 *datastore.Query )。

使用在单独进程中运行的模拟器的所有现有测试都通过。

更新 :

我将 Datastore 改为
Datastore datastore.Client

并在 Query 周围添加了一个包装器 datastore.Query :
Query datastore.Query

现在,Datastorer 接口(interface)包含
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))
}

在调用代码时,我使用
q := datastore.NewQuery(entity).Filter("Deleted =", false)
_, err := r.client.GetAll(ctx, (*Query)(q), data)

这将编译并且所有测试都通过。

关于go - 模拟 Go 数据库 SDK,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45941260/

10-16 10:02