如何测试/重构测试调用http.ListenAndServe的函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何测试/重构测试调用http.ListenAndServe的函数相关的知识,希望对你有一定的参考价值。

我正在学习并正在研究一种简单的服务,它从队列中提取一些数据并将其粘贴到数据库中。它还运行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
}

我能够很好地测试各个部分(如initDBvalidate),但我不清楚如何测试Run函数因为http.ListenAndServe阻塞。我终于出去了。以前,我会使用httpTest并创建一个测试服务器,但那时main将启动服务器(应用程序最初是更基本的)。

我要测试的一些东西:

我可以在启动时点击指标端点。一旦启动我就可以点击健康端点。我可以在队列上推送一条消息,一旦启动就收到它。 Run实际上开始没有恐慌。

一些注意事项:我正在使用docker来启动队列和数据库。测试Run函数的目的是确保引导有效并且应用程序可以成功运行。最终我想要通过队列推送数据并声明它已被正确处理。

问题:我应该如何测试或重构它以便它更容易端到端测试?

答案

您可以使用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方法添加到称为Services.httpServer.Shutdown()。这将导致调用s.httpServer.ListenAndServe返回错误http.ErrServerClosed

if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
    return err
}
return nil

在测试结束时关闭服务非常重要。否则,您将无法为您的服务进行多次单元测试。无论如何,清理资源都是良好的公民身份。

(3)您需要等到service.Run返回以确保服务实际关闭。

以上是关于如何测试/重构测试调用http.ListenAndServe的函数的主要内容,如果未能解决你的问题,请参考以下文章

修改/重构产品代码以支持集成测试的正确方法是啥?

读Java8函数式编程笔记08_测试调试和重构

HTTP API 自动化测试从手工测试到平台的演变

读重构一书的收获

测试/模拟从视图控制器上的方法内部调用为类方法的 UIAlertView

基于 BDD 理论的 Nebula 集成测试框架重构(上篇)