Golang中如何显式返回错误以测试http.Shutdown函数
Golang中如何显式返回错误以测试http.Shutdown函数 你好,
我测试了“正常”的测试用例,但想知道如何测试“异常”的测试用例。尝试传递一个已过期的上下文但没有成功。
谢谢
异常
func TestServer_Stop(t *testing.T) {
srv := New(":1234")
ctx, _ := context.WithTimeout(context.Background(), 1 * time.Millisecond)
time.Sleep(5 * time.Millisecond)
assert.Equal(t, "bad", srv.Stop(ctx)) // 这里返回的是 good
}
代码
func (s Server) Stop(ctx context.Context) string {
if e := s.Shutdown(ctx); e != nil {
return "bad"
} else {
return "good"
}
}
正常 这个没问题
func TestServer_Stop(t *testing.T) {
srv := New(":1234")
assert.Equal(t, "good", srv.Stop(context.Background()))
}
更多关于Golang中如何显式返回错误以测试http.Shutdown函数的实战教程也可以访问 https://www.itying.com/category-94-b0.html
2 回复
显然,Shutdown 成功是因为服务器关闭成功了。 查看下面的 Shutdown 代码,我们发现它只有在无法关闭所有连接时才会检查 ctx 错误。
为了让 Shutdown 返回错误,你需要有一个非空闲的连接。我建议你尝试执行一个 GET 操作但不读取数据。这可能会留下一个非空闲的连接。如果这还不够,可以尝试增加服务器返回的数据量,以防数据在客户端被缓冲。
此外,你可以创建一个带有截止时间(deadline)的上下文,并将时间设置为过去。这样可以避免需要休眠。
func (srv *Server) Shutdown(ctx context.Context) error {
atomic.StoreInt32(&srv.inShutdown, 1)
srv.mu.Lock()
lnerr := srv.closeListenersLocked()
srv.closeDoneChanLocked()
for _, f := range srv.onShutdown {
go f()
}
srv.mu.Unlock()
ticker := time.NewTicker(shutdownPollInterval)
defer ticker.Stop()
for {
if srv.closeIdleConns() {
return lnerr
}
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
}
}
}
更多关于Golang中如何显式返回错误以测试http.Shutdown函数的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
要显式返回错误以测试http.Shutdown函数,可以使用自定义的http.Server并模拟Shutdown行为。以下是两种方法:
方法1:使用接口和模拟实现
type server interface {
Shutdown(ctx context.Context) error
}
type realServer struct {
*http.Server
}
func (s *realServer) Shutdown(ctx context.Context) error {
return s.Server.Shutdown(ctx)
}
type mockServer struct {
shutdownErr error
}
func (m *mockServer) Shutdown(ctx context.Context) error {
return m.shutdownErr
}
func (s Server) Stop(ctx context.Context, server server) string {
if e := server.Shutdown(ctx); e != nil {
return "bad"
}
return "good"
}
func TestServer_Stop_Error(t *testing.T) {
srv := New(":1234")
mock := &mockServer{shutdownErr: errors.New("shutdown error")}
result := srv.Stop(context.Background(), mock)
assert.Equal(t, "bad", result)
}
方法2:直接注入错误(更直接)
func TestServer_Stop_WithError(t *testing.T) {
srv := New(":1234")
// 创建立即取消的上下文
ctx, cancel := context.WithCancel(context.Background())
cancel()
// 这会强制Shutdown返回上下文错误
result := srv.Stop(ctx)
assert.Equal(t, "bad", result)
}
方法3:使用nettest包模拟网络错误
import "golang.org/x/net/nettest"
func TestServer_Stop_NetworkError(t *testing.T) {
srv := New(":1234")
// 创建会超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond)
defer cancel()
// 确保上下文立即过期
time.Sleep(1 * time.Millisecond)
result := srv.Stop(ctx)
assert.Equal(t, "bad", result)
}
方法4:修改Server结构以支持测试注入
type Server struct {
server *http.Server
shutdownFunc func(ctx context.Context) error
}
func (s Server) Stop(ctx context.Context) string {
shutdown := s.shutdownFunc
if shutdown == nil {
shutdown = s.server.Shutdown
}
if e := shutdown(ctx); e != nil {
return "bad"
}
return "good"
}
func TestServer_Stop_WithMockedError(t *testing.T) {
srv := New(":1234")
srv.shutdownFunc = func(ctx context.Context) error {
return errors.New("simulated shutdown error")
}
result := srv.Stop(context.Background())
assert.Equal(t, "bad", result)
}
对于你的具体测试用例,问题在于http.Shutdown在上下文过期时可能不会立即返回错误。使用context.WithCancel并立即取消会更可靠:
func TestServer_Stop_Timeout(t *testing.T) {
srv := New(":1234")
ctx, cancel := context.WithCancel(context.Background())
cancel() // 立即取消上下文
// 给Shutdown一点时间处理
time.Sleep(10 * time.Millisecond)
assert.Equal(t, "bad", srv.Stop(ctx))
}

