【问题标题】:How to assert that the request happened when mocking an API concurrently?如何断言请求在同时模拟 API 时发生?
【发布时间】:2022-01-03 04:21:20
【问题描述】:

跟进问题至:How do I guarantee that the request happened correctly when mocking an API?

main.go

package main

import (
    "net/http"
)

func SomeFeature(host, a string) {
    if a == "foo" {
        resp, err := http.Get(host + "/foo")
    }
    if a == "bar" {
        resp, err := http.Get(host + "/baz"))
    }

    // baz is missing, the test should error!
}

main_test.go

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestSomeFeature(t *testing.T) {

    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(200)
    }))

    testCases := []struct {
        name     string
        variable string
    }{
        {
            name:     "test 1",
            variable: "foo",
        },
        {
            name:     "test 2",
            variable: "bar",
        },
        {
            name:     "test 3",
            variable: "baz",
        },
    }
    for _, tc := range testCases {
        tc := tc
        t.Run(tc.name, func(t *testing.T) {
            t.Parallel()
            SomeFeature(server.URL, tc.variable)

            // assert that the http call happened somehow?
        })
    }
}

同时保持测试并行/并发?

【问题讨论】:

  • 你得到并接受了最后一次被问到的答案,减去并发部分。您的示例代码未使用该答案中描述的任何方法,并且您再次询问有关检查是否进行了调用的相同问题。考虑到上一个问题的答案后,您能否澄清您的问题是什么以及您仍然缺少什么?
  • 上一个问题不处理并发,所以我正在寻找一种方法来处理它。据我所知,使用上一个答案中的变量会导致竞争条件,所以我想知道应该如何完成?我已经看到它使用多路复用器和通道完成,但我想知道正确的方法是什么。
  • 然后展示处理检查的实现,人们可以帮助您确保它同时工作。在您问题的示例中,没有进行这些检查,因此无法帮助更改它们的完成方式。
  • @Adrian 如果我不知道该怎么做,我该怎么做?举一个检查的例子,我必须实现这个答案stackoverflow.com/a/70102527/175071,这会破坏问题的目的还是我错了?
  • 它似乎不会破坏目的。你的问题似乎是“我如何同时做这件事”,如果你的示例代码正在做这件事,这更容易回答。

标签: go testing


【解决方案1】:

您可以为每个测试用例创建一个新服务器。


或者您可以使用通道,特别是通道映射,其中键是测试用例的标识符,例如

getChans := map[string]chan struct{}{}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    key := strings.Split(r.URL.Path, "/")[1] // extract channel key from path
    go func() { getChans[key] <- struct{}{} }()

    w.WriteHeader(200)
}))

向测试用例添加通道关键字段。这将被添加到主机的 URL 中,然后处理程序将提取密钥,如上所示,以获取正确的频道。还要添加一个字段来指示是否应该调用http.Get

testCases := []struct {
    name      string
    chkey     string
    variable  string
    shouldGet bool
}{
    {
        name:      "test 1",
        chkey:     "key1"
        variable:  "foo",
        shouldGet: true,
    },
    // ...
}

在运行测试用例之前,将特定于测试用例的通道添加到地图中:

getChans[tc.chkey] = make(chan struct{})

然后使用测试用例中的频道键字段作为主机URL路径的一部分:

err := SomeFeature(server.URL+"/"+tc.chkey, tc.variable)
if err != nil {
    t.Error("SomeFeature should not error")
}

要检查是否调用了 http.Get,请使用 select 并设置一些可接受的超时时间:

select {
case <-getChans[tc.chkey]:
    if !tc.shouldGet {
        t.Error(tc.name + " get called")
    }
case <-time.Tick(3 * time.Second):
    if tc.shouldGet {
        t.Error(tc.name + " get not called")
    }
}

https://go.dev/play/p/7By3ArkbI_o

【讨论】:

  • 只是出于好奇,您能想出一个不涉及超时的解决方案吗?
  • @PaimanRoointan 从我的脑海中消失,不,如果你选择使用频道。但是,您可以使用带有布尔映射的互斥锁来跟踪哪些测试用例触发了 GET 请求,如下所示:go.dev/play/p/e2Ttk0WmtNr
猜你喜欢
  • 1970-01-01
  • 2019-12-09
  • 1970-01-01
  • 1970-01-01
  • 2017-03-08
  • 2021-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多