【问题标题】:How to generate code coverage for REST service如何为 REST 服务生成代码覆盖率
【发布时间】:2019-09-18 10:22:30
【问题描述】:

我想获得用 Go 编写的 REST 服务的测试覆盖率。我通过 goroutine 生成 REST 服务,然后使用 REST 客户端发出 HTTP 请求,并查看 HTTP 响应。 测试成功通过,但 go test -cover 返回 0% 的测试覆盖率。 有没有办法获得 go lang REST 服务中使用的所有包的实际测试覆盖率。

我的测试文件: main_test.go

import (
    "testing"
)

// Test started when the test binary is started. Only calls main.
func TestSystem(t *testing.T) {
    go main()    // Spinning up the go lang REST server in a separate go routine.
    http.Post("https://localhost/do_something")
}

我的输出:

go test -cover  main_test.go
ok      command-line-arguments  0.668s  coverage: 0.0% of statements 

【问题讨论】:

  • 你启动了你的服务并向它发出了请求,但你甚至没有给一毫秒来处理这个请求。如果在http.Post 之后添加time.Sleep(time.Second) 会变成什么?尽管我建议您考虑将测试组织成套件:a) 使用 go main() 启动,b) 测试本身,c) 如果需要则拆除
  • 我怀疑问题在于您正在计算错误包的统计信息。使用-coverpkg 标志来指示要为哪些包计算覆盖率统计信息。见here
  • 另外,正如下面的 cmets 中所述,您调用 goroutine 的方式是完全错误的。您可能不应该在 goroutine 中运行 main()。相反,启动一个 goroutine 来启动您的应用程序,但可以在测试完成后取消,可能使用 context.Context
  • 这不是单元测试......它不是。老实说,运行所有内容并发出请求与 go run . &; curl 0.0.0.0:8080/do_something 之类的 bash 脚本相同
  • 为什么你忽略了http.Post的响应和错误检查?至少您需要添加resp, err:= http.Post("https://localhost/do_something") 并在之后检查resp and err 。也许您正在尝试在不存在的网址上发布?

标签: go code-coverage


【解决方案1】:

前言

这不可能是涵盖所有情况的完整答案。

这是试图解释如何从 OP 解释的内容中获得零覆盖率。

测试自己

测试成功通过,但 go test -cover 返回 0% 的测试覆盖率。

  1. 使用go main() 内部Test-function 启动服务
  2. 尝试http.Post 提供服务而不检查错误并且响应不是nil
  3. 测试期间没有恐慌,因此测试通过

测试中的问题

  1. 不保证服务在http.Post 尝试之前实际启动
  2. http.Post 的结果未被检查

第一个问题:

  • 坏/脏解决方案:在go main() 之后添加time.Sleep
  • 良好/干净的解决方案:仪器服务具有某种信号/监控服务循环已启动

第二个问题:

看看这样的测试函数:

func TestPost(t *testing.T) {
    var b bytes.Buffer
    http.Post("http:/inexistent.com/inexistent-url", "", &b)
    t.Log("No problems!")
}

此测试将始终通过。因为它只测试不存在恐慌。

为了让这个测试更正确:

func TestPost(t *testing.T) {
    var b bytes.Buffer
    if _, err:= http.Post("http:/inexistent.com/inexistent-url", "", &b); err!=nil{
    t.Errorf("http.Post : %v", err)
}

建议

  1. 查看http.Post结果
  2. 检查测试服务是否实际启动

【讨论】:

    【解决方案2】:

    感谢大家的回复。 我可以通过-coverpkg 获得覆盖。 不知道究竟是什么解决了下面的问题是@Flimzy 建议的对我有用的几个更改 1. 将 REST 服务器初始化代码从 main 移至函数。然后在测试用例中启动 REST 服务器,并通过向通道发送消息来优雅地终止它

        channel := make(chan error) // create a channel will listen for error
        go func(channel chan error) {
            inithandler.InitRestServer(channel, )
            _ = <-channel // terminate the channel
        }(channel)
    http.Post("https://localhost/do_something") // call the endpoint and resp verification
    .
    .
    channel <- errors.New("stop")
    

    使用 -coverpkg 触发测试

    go test main_test.go -cover -coverpkg ./...
    

    【讨论】:

      【解决方案3】:

      不管根本原因是什么,我认为这里的一般方法存在问题。

      我建议你在这里测试的一些东西不是你的代码,你不应该打扰。你不需要其实测试http协议和http包吧?

      所以我过去处理这个问题的方式是将我想要测试的有意义的代码从 http 处理程序函数中分离出来。 http 处理程序应该只负责验证输入,调用实际业务逻辑,然后格式化输出。

      以这种方式分解代码后,您可以直接从测试中调用有意义的函数。您的 http 处理程序函数应该非常简单,以至于没有任何 bug 潜伏的地方。

      不要寻求 100% 的测试覆盖率。测试什么是重要的。

      【讨论】:

      • golang 测试覆盖的工作原理是使用每行分配的原始代码。它不监视线程。阅读The cover story。我认为真正的问题是TestSubSystemhttp.Post 之后立即退出,甚至没有机会在测试服务中执行一行。
      • “很可能这里发生的事情是代码覆盖系统子系统只监视原始线程”——不,它根本不是这样工作的。
      • 感谢您提供封面故事的链接;我没见过这个。 ??
      • 我也不认为提前退出是解释。如果这是问题所在,您可能会得到 一些 覆盖率,而不是 0。但即便如此,goroutine 的完成方式显然是错误的。
      猜你喜欢
      • 1970-01-01
      • 2019-04-11
      • 1970-01-01
      • 2018-01-11
      • 2023-02-16
      • 2019-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多