4

对于句子

resp, err := client.Get(fmt.Sprintf("https://www.xxxxx/day?time=%s", time))

如果我想在单元测试中模拟对这个 client.Get() 的响应,我应该使用 httptest.server,但是如何将 url ( https://www.xxxxx/day?time=%s ) 绑定到httptest.server 的网址?这样当我调用 client.Get() 时,它可以返回我之前设置的响应。出于某种原因,我不能在这里模拟客户。

4

3 回答 3

3

你不,通常。您从服务器获取基本 URL 并将其提供给客户端:

package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
    "testing"
    "time"
)

func TestClient(t *testing.T) {
    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Verify request, send mock response, etc.
    }))

    defer server.Close()

    var client *http.Client
    var time time.Time

    baseURL := server.URL // Something like "http://127.0.0.1:53791"
    resp, err := client.Get(fmt.Sprintf(baseURL+"/day?time=%s", time))
    if err != nil {
            t.Fatal(err)
    }

    // Verify response body if applicable

    resp.Body.Close()
}
于 2017-10-26T08:48:45.443 回答
1

像这样

func NewTestServerWithURL(URL string, handler http.Handler) (*httptest.Server, error) {
    ts := httptest.NewUnstartedServer(handler)
    if URL != "" {
        l, err := net.Listen("tcp", URL)
        if err != nil {
            return nil, err
        }
        ts.Listener.Close()
        ts.Listener = l
    }
    ts.Start()
    return ts, nil
}
于 2019-05-07T20:27:09.697 回答
0

http.Client 是一个结构而不是一个接口,这使得模拟它变得很困难,如您所见。模拟它的另一种方法是传入例程所需的外部依赖项,因此不是直接使用 client.Get,而是使用 clientGet - 这是一个传递给例程的函数指针。

从单元测试中,您可以创建:

mockClientGet(c *http.client, url string) (resp *http.Response, err error) {
// add the test code to return what you want it to.
}

然后在您的主要代码中使用:

resp, err := clientGet(client, fmt.Sprintf("https://www.xxxxx/day?time=%s", time))

正常调用该过程时,使用指向 http.Client.Get 的函数指针,并为您的测试传递一个指向您的模拟的指针。这并不理想,但我还没有看到一个更好的方法来模拟非接口外部调用 - 并且考虑到它的外部依赖性,从外部注入它并不是一件坏事。

于 2017-10-26T07:56:56.447 回答