【问题标题】:How do you test a service that makes call to another API endpoint in Go?你如何测试在 Go 中调用另一个 API 端点的服务?
【发布时间】:2020-05-21 14:35:37
【问题描述】:

我有一个使用无限循环创建的简单服务来定期调用某个 HTTP API,在包aservice 中实现。我在那里创建了一个 Service 结构。通常,为了运行该服务,我公开了一个 StartService 方法,该方法用于同步运行该服务。然后,包的用户可以使用 goroutine 运行它。我的问题是,你如何为这种场景编写测试?

您是否运行整个系统并“模拟”API?我听说使用 3rd 方服务的代码不需要测试,但是整个 aservice 包可能只包含 StartServiceShutdown 方法。其余的是未导出的函数/方法,因此无法单独测试。如果是这样,那我根本不能写任何测试?

【问题讨论】:

  • 你在测试什么?您是否正在测试您从 API 获得的响应是​​否被正确处理?如果是这样,您可以将处理 API 输出的逻辑提取到未导出的函数并为这些函数编写测试。如果您正在测试对 API 的调用是否准备正确?然后模拟 API 并测试请求。
  • 是的,我实际上打算同时测试两者。但我认为这里的问题更多是 API 模拟。您的评论只是给了我一个关于测试场景的答案。也许给我一个关于模拟部分的细节?如果您确实模拟 API,这意味着您需要单独启动一个服务器,对吗?尤其是对于并行测试,这不是有点混乱吗?
  • 您可以使用 net/http/httptest 包在测试服务器上模拟它。或者,您可以通过将 HTTP 部分重构为一个结构,并在测试期间传递该结构的单独实现来模拟没有 HTTP 位的 API 调用

标签: go tdd bdd


【解决方案1】:

使用 Go,您将在模拟外部 http 请求时获得很棒的体验。长话短说,只需用 net/http/httptest 包中的服务器 url 替换基本 url。 您可以模仿 Google 模拟其外部请求的方式,例如在 google maps here 中探索测试。

server := mockServer(200, response)
defer server.Close()
c, _ := NewClient(WithAPIKey(apiKey), WithBaseURL(server.URL))
r := &DirectionsRequest{
    Origin:      "Google Sydney",
    Destination: "Glebe Pt Rd, Glebe",
    Mode:        TravelModeTransit,
}

resp, _, err := c.Directions(context.Background(), r) 
// your assertions goes here


 // Create a mock HTTP Server that will return a response with HTTP code and body.
func mockServer(code int, body string) *httptest.Server {
    server := mockServerForQuery("", code, body)
    return server.s
}

func mockServerForQuery(query string, code int, body string) *countingServer {
    server := &countingServer{}

    server.s = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if query != "" && r.URL.RawQuery != query {
            dmp := diffmatchpatch.New()
            diffs := dmp.DiffMain(query, r.URL.RawQuery, false)
            log.Printf("Query != Expected Query: %s", dmp.DiffPrettyText(diffs))
            server.failed = append(server.failed, r.URL.RawQuery)
            http.Error(w, "fail", 999)
            return
        }
        server.successful++

        w.WriteHeader(code)
        w.Header().Set("Content-Type", "application/json; charset=UTF-8")
        fmt.Fprintln(w, body)
    }))

    return server
}

【讨论】:

  • 为 httptest 包点赞,这样我就不必生成单独的服务器了,对吧?如果我必须生成一个单独的虚拟服务器,特别是对于使用 CI 服务器运行它来说有点复杂。这种测试还叫单元测试?还是行为测试?
猜你喜欢
  • 2018-10-20
  • 1970-01-01
  • 1970-01-01
  • 2019-03-06
  • 1970-01-01
  • 2019-04-19
  • 2017-04-10
  • 2019-02-19
  • 1970-01-01
相关资源
最近更新 更多