Golang中如何测试使用通道和信号实现的优雅HTTP服务器关闭
Golang中如何测试使用通道和信号实现的优雅HTTP服务器关闭 你好,
这是我的应用程序的主入口点,我想对其进行测试。然而,鉴于我接触Go语言才几天,我正在努力为其编写测试。从技术上讲,它会启动HTTP服务器,并在收到信号时优雅地关闭。请问我该如何测试这个功能?
谢谢
func main() {
srv := http.NewServer()
log.Print("app starting")
shutChan := make(chan struct{})
signChan := make(chan os.Signal, 1)
signal.Notify(signChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
go listen(cancel, signChan)
// This calls ListenAndServe behind the scene
if err := srv.Start(ctx); err != nil {
panic(err)
}
<-shutChan
log.Print("app shutdown")
}
func listen(cancel context.CancelFunc, signChan chan os.Signal) {
sig := <-signChan
log.Printf("%v signal received", sig)
cancel()
}
更多关于Golang中如何测试使用通道和信号实现的优雅HTTP服务器关闭的实战教程也可以访问 https://www.itying.com/category-94-b0.html
要测试 main 函数中的某些内容,你必须将 main 中的代码放入另一个函数中,然后你就可以从测试函数中调用该函数。
将以下代码:
func main() {
do things ...
}
修改为:
func main() {
foo()
}
func foo() {
do things ...
}
func TestFoo(t *testing.T) {
foo()
if state != expected {
t.Error("unexpected state")
}
}
您是指使用 go test 调用的单元测试进行测试吗?这只有在能够通过代码生成 SIGINT 或 SIGTERM 信号时才可能实现。我不确定这是否可行(signChan<-)。此外,无法使用 go test 测试 main 函数。要做到这一点,您必须将代码封装到可以测试的函数中。
出于这些原因,我会通过运行代码并输入 ctrl-C 来手动测试这段代码。
顺便提一下,我在 http 文档或源代码中找不到 Start() 方法。通常,您应该在监听函数中调用 srv.Shutdown(ctx),并且给定的上下文通常是超时上下文,因为 Shutdown 会阻塞直到所有连接都返回空闲状态。这并非不可能永远阻塞。
还要注意,os.Interrupt 等于 syscall.SIGINT(定义在 os/exec_posix.go 中)。
感谢您的建议。
是的,我的意思是我希望用 go test 命令进行测试。目前我正按照您的建议进行手动测试,整个过程运行良好。由于我还在学习阶段,所以不知道如何在单元测试中模拟手动测试。
Start() 函数实际上触发了 ListenAndServe,这个函数是代码最开头 http.NewServer() 的一部分,因此它是一个包装器。您可以在这里看到它,这是您之前回答过的问题。
不过,我仍然需要以某种方式测试 cmd/api/main.go 中的内容。如果您想知道我的应用程序是什么样子,请看下面。我是一名学习者,所以欢迎任何改进建议。
cmd/api/main.go
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/joho/godotenv"
"myapp/internal/app"
"myapp/internal/http"
)
func main() {
srv := http.NewServer()
log.Print("app starting")
signChan := make(chan os.Signal, 1)
signal.Notify(signChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
ctx, cancel := context.WithCancel(context.Background())
go listen(cancel, signChan)
if err := app.New(srv).Start(ctx); err != nil {
panic(err)
}
log.Print("app shutdown")
}
func listen(cancel context.CancelFunc, signChan chan os.Signal) {
sig := <-signChan
log.Printf("%v signal received", sig)
cancel()
}
internal/app/api.go
package app
import (
"context"
"fmt"
"log"
"time"
"myapp/internal/http"
)
type App struct {
srv http.Server
}
func New(srv http.Server) App {
return App{srv: srv}
}
func (a App) Start(ctx context.Context) error {
shutChan := make(chan struct{})
go shutdown(ctx, shutChan, a)
if err := a.srv.Start(); err != nil {
return fmt.Error("http: failed to start")
}
<-shutChan
return nil
}
func shutdown(ctx context.Context, shutChan chan<- struct{}, a App) {
<-ctx.Done()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := a.srv.Shutdown(ctx)
if err != nil {
log.Print("http: interrupted some active connections")
} else {
log.Print("http: served all active connections")
}
close(shutChan)
}
internal/http/server.go
package http
import (
"net/http"
)
type Server struct {
*http.Server
}
func NewServer(adr string) Server {
return Server{
&http.Server{Addr: adr},
}
}
func (s Server) Start() error {
if err := s.ListenAndServe(); err != http.ErrServerClosed {
return err
}
return nil
}


