douqie1852 2019-03-18 12:19
浏览 16

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

I am trying to test some golang code and I have a method that calls several other methods from its body. All these methods perform some kind of operations using an elastic search client. I wanted to know whether it will be a good practice if I used a test server for testing this method that will write different responses depending upon the request method and path it received from the request that is made when the methods inside the body execute and make calls to the elasticsearch client that sends the requests to my test server?

Update:

I am testing an elasticsearch middleware. It implements a reindex service like this

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

I can easily test all the methods using this pattern. Creating a simple elastic search client using a httptest server url and making requests to that server

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
", tt.err, err)
            }
        })
    }
}

But in case of reindex method this approach poses a problem since reindex makes calls to all the other methods inside its body. reindex looks something like this:

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
}

For testing the reindex method I have tried mocking and DI but that turns out to be hard since the methods are defined on a struct instead of passing an interface as an argument to them. (So now I want to keep the implementation same since it would require making changes to all the plugin implementations and I want to avoid that)

I wanted to know whether I can use a modified version of my build server funtion (the one I am using is given below) to return responses for different methods for the reindex service which will write the appropriate responses based on the HTTP method and the request path that is used by that method?


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
", setup.Path, r.URL.EscapedPath())
        }*/
        if !matched {
            t.Fatalf("No requests matched setup. Got method %s, Path %s, body %s
", r.Method, r.URL.EscapedPath(), requestBody)
        }
    })

    return httptest.NewServer(handlerFunc)
}

Something like this function but it takes a map of request methods and past mapped to appropriate responses and writes them to the writer?

  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
    • ¥20 怎么用dlib库的算法识别小麦病虫害
    • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
    • ¥15 java写代码遇到问题,求帮助
    • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
    • ¥15 有了解d3和topogram.js库的吗?有偿请教
    • ¥100 任意维数的K均值聚类
    • ¥15 stamps做sbas-insar,时序沉降图怎么画
    • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
    • ¥15 关于#Java#的问题,如何解决?