我在Go中构建了一个快速简便的API,用于查询ElasticSearch。既然我知道它可以完成,我想通过添加测试来正确地完成它。我已经抽象了一些代码,以便它可以进行单元测试,但是我一直有一些模拟弹性库的问题,因此我认为如果我尝试一个简单的案例来嘲笑那就最好了。
import (
"encoding/json"
"github.com/olivere/elastic"
"net/http"
)
...
func CheckBucketExists(name string, client *elastic.Client) bool {
exists, err := client.IndexExists(name).Do()
if err != nil {
panic(err)
}
return exists
}
现在测试......
import (
"fmt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"testing"
)
type MockClient struct {
mock.Mock
}
func (m *MockClient) IndexExists(name string) (bool, error) {
args := m.Mock.Called()
fmt.Println("This is a thing")
return args.Bool(0), args.Error(1)
}
func TestMockBucketExists(t *testing.T) {
m := MockClient{}
m.On("IndexExists", "thisuri").Return(true)
>> r := CheckBucketExists("thisuri", m)
assert := assert.New(t)
assert.True(r, true)
}
我对此产生了以下错误:cannot use m (type MockClient) as type *elastic.Client in argument to CheckBucketExists
。
我认为这是我使用elastic.client类型的基础,但我仍然是一个太多的菜鸟。
答案 0 :(得分:1)
该行
func CheckBucketExists(name string, client *elastic.Client) bool {
指出CheckBucketExists
期望*elastic.Client
。
行:
m := MockClient{}
m.On("IndexExists", "thisuri").Return(true)
r := CheckBucketExists("thisuri", m)
将MockClient
传递给CheckBucketExists
函数。
这会导致类型冲突。
也许您需要将github.com/olivere/elastic
导入测试文件并执行:
m := &elastic.Client{}
而不是
m := MockClient{}
但我并不是百分之百确定你要做什么。
答案 1 :(得分:0)
这是一个老问题,但也无法找到解决方案。 不幸的是,这个库是使用一个结构实现的,这使得模拟它根本不是微不足道的,所以我找到的选项是:
(1)在您自己的接口上包装所有elastic.SearchResult
方法,并且#34;代理"电话,所以你最终会得到:
type ObjectsearchESClient interface {
// ... all methods...
Do(context.Context) (*elastic.SearchResult, error)
}
// NewObjectsearchESClient returns a new implementation of ObjectsearchESClient
func NewObjectsearchESClient(cluster *config.ESCluster) (ObjectsearchESClient, error) {
esClient, err := newESClient(cluster)
if err != nil {
return nil, err
}
newClient := objectsearchESClient{
Client: esClient,
}
return &newClient, nil
}
// ... all methods...
func (oc *objectsearchESClient) Do(ctx context.Context) (*elastic.SearchResult, error) {
return oc.searchService.Do(ctx)
}
然后像应用程序的其他模块一样模拟此界面和响应。
(2)另一个选项就像在this blog post中指出的那样,使用httptest.Server
嘲笑来自其余调用的响应
为此,我嘲笑了处理程序,它包括模拟来自" HTTP调用的响应"
func mockHandler () http.HandlerFunc{
return func(w http.ResponseWriter, r *http.Request) {
resp := `{
"took": 73,
"timed_out": false,
... json ...
"hits": [... ]
...json ... ,
"aggregations": { ... }
}`
w.Write([]byte(resp))
}
}
然后你创建一个虚拟的elastic.Client结构
func mockClient(url string) (*elastic.Client, error) {
client, err := elastic.NewSimpleClient(elastic.SetURL(url))
if err != nil {
return nil, err
}
return client, nil
}
在这种情况下,我建立了一个构建弹性.SearchService的库并返回它,所以我使用HTTP,如:
...
ts := httptest.NewServer(mockHandler())
defer ts.Close()
esClient, err := mockClient(ts.URL)
ss := elastic.NewSearchService(esClient)
mockLibESClient := es_mock.NewMockSearcherClient(mockCtrl)
mockLibESClient.EXPECT().GetEmployeeSearchServices(ctx).Return(ss, nil)
其中mockLibESClient是我提到的库,我们存根mockLibESClient.GetEmployeeSearchServices
方法,使它返回SearchService,返回预期的有效负载。
注意:为了创建模拟mockLibESClient,我使用了https://github.com/golang/mock
我觉得这很复杂,但是"包装"弹性。客户在我看来更多的工作。
问题:我试图通过使用https://github.com/vburenin/ifacemaker
来创建一个接口来模拟它,然后使用https://github.com/golang/mock
模拟该接口并使用它,但在尝试返回时遇到了兼容性错误界面而不是结构,我不是一个Go期望,所以我可能需要更好地理解类型转换,以便能够像这样解决它。所以,如果你们中的任何一个人知道怎么做,请告诉我。