如何使 go 测试彼此独立

问题描述 投票:0回答:1

我有两个 Go 测试函数,其中一个的执行会导致另一个失败。
我很清楚,标准 http 库保存了一些全局变量或状态,这些变量或状态在多次测试中仍然存在。对于此类情况,建议具有一些清理功能。不过我认为测试通常应该不会互相影响。这有点违背了重点。

最小可重复样品

main_test.go

package main

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

func start_server(t *testing.T) {
    err := http.ListenAndServe(":8090", nil)
    if err != nil {
        t.Errorf("server shut down: %s\n", err.Error())
    }
}

func TestA(t *testing.T) {
    go start_server(t)
    timer := time.NewTimer(time.Second)
    <-timer.C
}

func TestB(t *testing.T) {
    go start_server(t)
    timer := time.NewTimer(time.Second)
    <-timer.C
}

运行这两个测试:

$ go test -v main_test.go
=== RUN   TestA
--- PASS: TestA (1.00s)
=== RUN   TestB
    main_test.go:12: server shut down: listen tcp :8090: bind: address already in use
--- FAIL: TestB (1.00s)
FAIL
FAIL    command-line-arguments  2.005s
FAIL

但是,当仅执行第二个测试时:

$ go test -v main_test.go -run TestB
=== RUN   TestB
--- PASS: TestB (1.00s)
PASS
ok      command-line-arguments  1.006s

我的问题是,如何使测试完全独立于其他测试执行?并行化不是必需的。

go testing server
1个回答
0
投票

正如其他人在评论中提到的,使用

net/http/httptest
是最好的路线。

它不是一个“模拟”服务器——它将进行路由匹配、运行处理程序等等。它只是不会绑定到网络地址来执行此操作。

如果您确实需要适合您的用例的网络服务,请创建您自己的

net.Listener
而不是使用
http.ListenAndServe
。这使您可以绑定到随机的、未分配的端口并避免测试之间的冲突。

您的示例启动了一个运行

TestA
但从未关闭的服务器。当
TestB
尝试绑定到同一端口时,它无法绑定。遵循以下方法之一将解决该问题:

func startServerTest(t testing.TB) *http.Client {
    t.Helper()

    // Add your handler here
    srv := httptest.NewServer(nil)

    // Close the server whenever the test/sub-test finishes
    t.Cleanup(func() {
        srv.Close()
    })
    return srv.Client()
}

func TestA(t *testing.T) {
    client := startServerTest(t)

    url := "..."
    _, err := client.Get(url)
    if err != nil {
        t.Errorf("could not issue request: %v", err)
    }
}

func startServerReal(t testing.TB) string {
    t.Helper()
    // port '0' means find any available port
    //
    // Use lis.Addr() to access the socket that was bound
    lis, err := net.Listen("tcp", "localhost:0")
    if err != nil {
        t.Errorf("could not start server: %v", err)
        return ""
    }

    // Close the server whenever the test/sub-test finishes
    t.Cleanup(func() {
        lis.Close()
    })

    // Add your handler here
    go http.Serve(lis, nil)
    return lis.Addr().String()
}

func TestB(t *testing.T) {
    addr := startServerReal(t)
    url := url.URL{
        Scheme: "http",
        Host:   addr,
        Path:   "/",
    }

    _, err := http.Get(url.String())
    if err != nil {
        t.Errorf("could not issue request: %v", err)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.