#go #testing
#Вперед #тестирование
Вопрос:
Я пытаюсь протестировать некоторый код golang, и у меня есть метод, который вызывает несколько других методов из его тела. Все эти методы выполняют какие-то операции с использованием клиента эластичного поиска. Я хотел знать, будет ли хорошей практикой, если я использую тестовый сервер для тестирования этого метода, который будет записывать разные ответы в зависимости от метода запроса и пути, который он получил от запроса, который выполняется, когда методы внутри тела выполняются и вызывают клиент elasticsearch, который отправляет запросы на мой тестовый сервер?
Обновить:
Я тестирую промежуточное программное обеспечение 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)
}
Я могу легко протестировать все методы, используя этот шаблон. Создание простого клиента elastic search с использованием URL сервера httptest и выполнение запросов к этому серверу
var createIndexTests = []struct {
setup *ServerSetup
index string
err string
}{
{
amp;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 insteadn", 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
}
Для тестирования метода переиндексации я пробовал mocking и DI, но это оказалось сложным, поскольку методы определены в структуре вместо того, чтобы передавать им интерфейс в качестве аргумента. (Итак, теперь я хочу сохранить реализацию такой же, поскольку это потребовало бы внесения изменений во все реализации плагинов, и я хочу избежать этого)
Я хотел знать, могу ли я использовать модифицированную версию моей функции сервера сборки (ту, которую я использую, приведена ниже) для возврата ответов для разных методов для службы переиндексации, которая будет записывать соответствующие ответы на основе метода HTTP и пути запроса, используемого этим методом?
type ServerSetup struct {
Method, Path, Body, Response string
HTTPStatus int
}
// This function is a modified version of: https://github.com/github/vulcanizer/blob/master/es_test.go
func buildTestServer(t *testing.T, setup *ServerSetup) *httptest.Server {
handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestBytes, _ := ioutil.ReadAll(r.Body)
requestBody := string(requestBytes)
matched := false
if r.Method == setup.Method amp;amp; r.URL.EscapedPath() == setup.Path amp;amp; requestBody == setup.Body {
matched = true
if setup.HTTPStatus == 0 {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(setup.HTTPStatus)
}
_, err := w.Write([]byte(setup.Response))
if err != nil {
t.Fatalf("Unable to write test server response: %v", err)
}
}
// TODO: remove before pushing
/*if !reflect.DeepEqual(r.URL.EscapedPath(), setup.Path) {
t.Fatalf("wanted: %s got: %sn", setup.Path, r.URL.EscapedPath())
}*/
if !matched {
t.Fatalf("No requests matched setup. Got method %s, Path %s, body %sn", r.Method, r.URL.EscapedPath(), requestBody)
}
})
return httptest.NewServer(handlerFunc)
}
Что-то вроде этой функции, но она берет карту методов запроса и прошлого, сопоставленных с соответствующими ответами, и записывает их в writer?
Комментарии:
1. На этот вопрос очень сложно ответить, поскольку это в первую очередь зависит от функциональности, которую вы пытаетесь протестировать.
2. @RickyA Я добавил более подробную информацию о реализации и кодовой базе в описание