我正在测试Elasticsearch中间件。它实现了这样的重新索引服务
type reindexService interface {
reindex(ctx context.Context, index string, mappings, settings map[string]interface{}, includes, excludes, types []string) error
mappingsOf(ctx context.Context, index string) (map[string]interface{}, error)
settingsOf(ctx context.Context, index string) (map[string]interface{}, error)
aliasesOf(ctx context.Context, index string) ([]string, error)
createIndex(ctx context.Context, name string, body map[string]interface{}) error
deleteIndex(ctx context.Context, name string) error
setAlias(ctx context.Context, index string, aliases ...string) error
getIndicesByAlias(ctx context.Context, alias string) ([]string, error)
}
我可以轻松地使用此模式测试所有方法。使用httptest服务器网址创建一个简单的弹性搜索客户端,并向该服务器发出请求
var createIndexTests = []struct {
setup *ServerSetup
index string
err string
}{
{
&ServerSetup{
Method: "PUT",
Path: "/test",
Body: `null`,
Response: `{"acknowledged": true, "shards_acknowledged": true, "index": "test"}`,
},
"test",
"",
},
// More test cases here
}
func TestCreateIndex(t *testing.T) {
for _, tt := range createIndexTests {
t.Run("Should successfully create index with a valid setup", func(t *testing.T) {
ctx := context.Background()
ts := buildTestServer(t, tt.setup)
defer ts.Close()
es, _ := newTestClient(ts.URL)
err := es.createIndex(ctx, tt.index, nil)
if !compareErrs(tt.err, err) {
t.Fatalf("Index creation should have failed with error: %v got: %v instead\n", tt.err, err)
}
})
}
}
但是在reindex
方法的情况下,此方法带来了一个问题,因为reindex对其主体内的所有其他方法进行了调用。重新索引看起来像这样:
func (es *elasticsearch) reindex(ctx context.Context, indexName string, mappings, settings map[string]interface{}, includes, excludes, types []string) error {
var err error
// Some preflight checks
// If mappings are not passed, we fetch the mappings of the old index.
if mappings == nil {
mappings, err = es.mappingsOf(ctx, indexName)
// handle err
}
// If settings are not passed, we fetch the settings of the old index.
if settings == nil {
settings, err = es.settingsOf(ctx, indexName)
// handle err
}
// Setup the destination index prior to running the _reindex action.
body := make(map[string]interface{})
body["mappings"] = mappings
body["settings"] = settings
newIndexName, err := reindexedName(indexName)
// handle err
err = es.createIndex(ctx, newIndexName, body)
// handle err
// Some additional operations
// Reindex action.
_, err = es.client.Reindex().
Body(reindexBody).
Do(ctx)
// handle err
// Fetch all the aliases of old index
aliases, err := es.aliasesOf(ctx, indexName)
// handle err
aliases = append(aliases, indexName)
// Delete old index
err = es.deleteIndex(ctx, indexName)
// handle err
// Set aliases of old index to the new index.
err = es.setAlias(ctx, newIndexName, aliases...)
// handle err
return nil
}
我当时正在考虑模拟reindex所使用的函数,并以某种方式将其注入es
结构中,以便在reindex
内部调用模拟,但应调用原始reindex函数,因为我想要原始函数功能涵盖在我的测试中。
我已经阅读了很多有关模拟和DI的文章,但是我仍然无法解决这个问题。例如如果我有一个类似的模拟es结构,该结构实现了相同的接口,而我想在测试reindex方法时使用它
type mockES struct {
url string
client *elastic.Client
}
func newMockElasticsearch(url string) (*mockES, error) {
client, err := elastic.NewSimpleClient(elastic.SetURL(url))
if err != nil {
return nil, fmt.Errorf("error while initializing elastic client: %v", err)
}
es := &mockES{
url: url,
client: client,
}
return es, nil
}
func (m *mockES) mappingsOf(ctx context.Context, index string) (map[string]interface{}, error) {
return m.mockMappingsOf(ctx, index), nil
}
func (m *mockES) mockMappingsOf(ctx context.Context, index string) map[string]interface{} {
// return some pre-defined response
}
我不明白的是,如果我调用在ockES上定义的重新索引,则只能使用这些模拟方法,但我想调用在elasticsearch结构上定义的原始reindex方法以及对模拟方法可以采用其他方法。
考虑到当前代码库的实现状态,是否有可能实现它?或者我必须更改实现的设计以便能够为其编写测试?