我正在学习go并且正在开发一个简单的服务,该服务从队列中提取一些数据并将其粘贴到数据库中。它还运行一个Web服务器以允许抓取数据。现在我有两个go文件(为简洁起见,省略了一些文本):
func main() {
parseConfig()
s := &Service{ServiceConfig: config}
err := s.Run()
if err != nil {
panic(err)
}
}
然后是服务的定义(为简洁起见,省略了一些内容):
func (s *Service) Run() error {
if err := s.validate(); err != nil {
return err
}
if err := s.initDB(); err != nil {
return err
}
defer s.db.Close()
// Same pattern with health check library (init, start, close)
// Same pattern starting queue consumer (init, start, close)
s.mux = http.NewServeMux()
s.registerHandlers(s.mux)
http.ListenAndServe(":8080", s.mux)
return nil
}
和结构
type Service struct {
Config // Hold db connection info
db *sql.DB
hc *health
}
我能够很好地测试各个部分(例如initDB
或validate
),但是由于http.ListenAndServe块,我不清楚如何测试Run
函数。我最终超时了。以前,我将使用httpTest并创建一个测试服务器,但是那是main
启动服务器的时候(应用程序起初比较基础)。
一些我要测试的东西:
启动后,我可以点击指标终点。
一旦启动,我就可以达到健康终点。
我可以将消息推送到队列中,并且一旦开始就可以接收它。
Run
实际上开始时没有发生恐慌。
一些注意事项:我正在使用docker启动队列和数据库。测试Run
函数的目的是确保引导程序可以正常工作,并且应用程序可以成功运行。最终,我将要通过队列推送数据并断言其已正确处理。
问题:我应该如何测试或重构它以便更容易地进行端到端的测试?
答案 0 :(得分:1)
您可以使用goroutine构建测试工具以在单元测试中执行Run
:
func TestRun(t *testing.T) {
service := Service{}
serviceRunning := make(chan struct{})
serviceDone := make(chan struct{})
go func() {
close(serviceRunning)
err := service.Run()
defer close(serviceDone)
}()
// wait until the goroutine started to run (1)
<-serviceRunning
//
// interact with your service to test whatever you want
//
// stop the service (2)
service.Shutdown()
// wait until the service is shutdown (3)
<-serviceDone
}
这只是一个基本示例,以显示原则上该如何完成。在生产中应改进以下几点:
(0)最重要的细节:请勿在生产环境中使用http.ListenAndServe
!而是创建自己的http.Server
实例。这样可以省去很多麻烦。
s.httpServer := http.Server {
Addr: ":8080",
Handler: s.mux,
}
(1)应该将服务正在运行的指示移到您的Service
类型中。 Run
中的初始化部分可能需要一段时间,并且指示通道应该在调用ListenAndServe
之前关闭:
close(s.Running)
s.httpServer.ListenAndServe()
当然,您需要在服务类型中添加指示频道。
(2)向Shutdown
添加一个Service
方法,该方法调用s.httpServer.Shutdown()
。这将导致对s.httpServer.ListenAndServe
的调用返回并返回错误http.ErrServerClosed
。
if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}
return nil
在测试结束时关闭服务很重要。否则,您将不能对服务进行多个单元测试。无论如何,清理资源都是好公民。
(3)您需要等到service.Run
返回以确保该服务实际上已关闭。