Golang中如何测试HTTP ListenAndServe的包装函数
Golang中如何测试HTTP ListenAndServe的包装函数 你好,
我在想如何测试下面的 Start 方法,或者我是否真的应该测试它?如果我在测试中直接调用 Start,它会挂起,因为我认为它实际上启动了服务器,而这正是我们想要避免的。
谢谢
package http
import (
"net/http"
)
type Server struct {
*http.Server
}
func NewServer(adr string) Server {
return Server{
&http.Server{
Addr: adr,
Handler: nil,
},
}
}
func (s Server) Start() error {
if err := s.ListenAndServe(); err != http.ErrServerClosed {
return err
}
return nil
}
func TestServer_Start(t *testing.T) {
// ???
}
更多关于Golang中如何测试HTTP ListenAndServe的包装函数的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
非常感谢 @Christophe_Meessen。这对我的学习帮助很大。
更多关于Golang中如何测试HTTP ListenAndServe的包装函数的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
测试函数可以启动一个Go协程,在休眠1秒后调用Shutdown。这样你就可以在测试中调用Start函数。但是你真的需要测试如此简单的函数吗?
以下是一个示例代码,它将在1秒后调用Shutdown并停止服务器。
func TestServer_Start(t *testing.T) {
srv := NewServer(":45566")
go func () {
time.Sleep(1*time.Second)
srv.Shutdown(context.Background())
}()
err := srv.Start()
if err != nil {
t.Error("unexpected error:", err)
}
}
要测试 Start 方法,可以通过启动服务器后立即关闭它来避免测试挂起。这里是一个示例测试:
package http
import (
"context"
"net/http"
"testing"
"time"
)
func TestServer_Start(t *testing.T) {
server := NewServer(":0") // 使用 :0 让系统分配随机端口
// 在 goroutine 中启动服务器
go func() {
if err := server.Start(); err != nil && err != http.ErrServerClosed {
t.Errorf("Start failed: %v", err)
}
}()
// 给服务器一点时间启动
time.Sleep(100 * time.Millisecond)
// 优雅关闭服务器
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
t.Errorf("Shutdown failed: %v", err)
}
}
如果需要测试实际的 HTTP 请求处理,可以这样写:
func TestServer_StartWithHandler(t *testing.T) {
// 创建自定义 handler 的服务器
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
server := &http.Server{
Addr: ":0",
Handler: handler,
}
wrappedServer := Server{server}
// 获取实际监听的地址
listener, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
server.Addr = listener.Addr().String()
// 启动服务器
go func() {
if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
t.Errorf("Serve failed: %v", err)
}
}()
// 给服务器启动时间
time.Sleep(100 * time.Millisecond)
// 测试 HTTP 请求
resp, err := http.Get("http://" + server.Addr + "/")
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status 200, got %d", resp.StatusCode)
}
// 关闭服务器
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server.Shutdown(ctx)
}
对于 Start 方法中错误处理的测试:
func TestServer_Start_Error(t *testing.T) {
// 创建已经在使用的端口
listener, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
// 尝试在相同端口启动服务器
server := NewServer(listener.Addr().String())
// 这个应该快速失败
errCh := make(chan error, 1)
go func() {
errCh <- server.Start()
}()
select {
case err := <-errCh:
if err == nil {
t.Error("Expected error when starting server on used port")
}
case <-time.After(1 * time.Second):
t.Error("Start should have failed immediately")
}
}
这些测试方法可以验证 Start 方法的基本功能,同时避免测试无限挂起。

