测试技巧:网络测试

1. TCP/HTTP

假设需要测试某个 API 接口的 handler 能够正常工作,例如 helloHandler

func helloHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello world"))
}

那我们可以创建真实的网络连接进行测试:

// test code
import (
    "io/ioutil"
    "net"
    "net/http"
    "testing"
)

func handleError(t *testing.T, err error) {
    t.Helper()
    if err != nil {
        t.Fatal("failed", err)
    }
}

func TestConn(t *testing.T) {
    ln, err := net.Listen("tcp", "127.0.0.1:0")
    handleError(t, err)
    defer ln.Close()

    http.HandleFunc("/hello", helloHandler)
    go http.Serve(ln, nil)

    resp, err := http.Get("http://" + ln.Addr().String() + "/hello")
    handleError(t, err)

    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    handleError(t, err)

    if string(body) != "hello world" {
        t.Fatal("expected hello world, but got", string(body))
    }
}
  • net.Listen("tcp", "127.0.0.1:0"):监听一个未被占用的端口,并返回 Listener。
  • 调用 http.Serve(ln, nil) 启动 http 服务。
  • 使用 http.Get 发起一个 Get 请求,检查返回值是否正确。
  • 尽量不对 httpnet 库使用 mock,这样可以覆盖较为真实的场景。

2. httptest

针对 http 开发的场景,使用标准库 net/http/httptest 进行测试更为高效。

上述的测试用例改写如下:

// test code
import (
    "io/ioutil"
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestConn(t *testing.T) {
    req := httptest.NewRequest("GET", "http://example.com/foo", nil)
    w := httptest.NewRecorder()
    helloHandler(w, req)
    bytes, _ := ioutil.ReadAll(w.Result().Body)

    if string(bytes) != "hello world" {
        t.Fatal("expected hello world, but got", string(bytes))
    }
}

使用 httptest 模拟请求对象(req)和响应对象(w),达到了相同的目的。

END 链接