将同一个httptest服务器用于多种方法是否是一个好习惯

时间:2019-03-18 12:19:20

标签: 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)
}

我可以轻松地使用此模式测试所有方法。使用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方法,我尝试了模拟和DI,但是由于方法是在结构上定义的,而不是将接口作为参数传递给它们,因此这变得很困难。 (所以现在我想保持实现不变,因为这需要对所有插件实现进行更改,因此我想避免这种情况)

我想知道是否可以使用构建服务器功能的修改版本(下面使用的版本)来返回针对reindex服务的不同方法的响应,这些方法将基于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 && r.URL.EscapedPath() == setup.Path && 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: %s\n", setup.Path, r.URL.EscapedPath())
        }*/
        if !matched {
            t.Fatalf("No requests matched setup. Got method %s, Path %s, body %s\n", r.Method, r.URL.EscapedPath(), requestBody)
        }
    })

    return httptest.NewServer(handlerFunc)
}

类似此功能的东西,但它需要一个请求方法的映射,并将过去映射到适当的响应,然后将其写入编写器?

0 个答案:

没有答案