如何在模拟 API 并发时断言请求是否发生?

huangapple go评论74阅读模式
英文:

How to assert that the request happened when mocking an API concurrently?

问题

对于您提供的代码和问题,我将为您进行翻译:

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!
}
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?
		})
	}
}

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

希望对您有所帮助!

英文:

Followup question to: https://stackoverflow.com/questions/70087909/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?
		})
	}
}
  • GO Playground: https://go.dev/play/p/EFanSSzgnbk
  • How to do I assert that each test case send a request to the mocked server?
  • How can I assert that a request wasn't sent?

All while keeping the tests parallel/concurrent?

答案1

得分: 3

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

或者你可以使用通道,具体来说是一个通道映射,其中键是测试用例的标识符,例如:

getChans := map[string]chan struct{}{}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    key := strings.Split(r.URL.Path, "/")[1] // 从路径中提取通道键
    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

英文:

You could create a new server for each test case.


Or you can use channels, specifically a map of channels where the key is the test case's identifier, e.g.

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

	w.WriteHeader(200)
}))

Add a channel key field to the test case. This will be added to the host's URL and the handler will then extract the key, as demonstrated above, to get the correct channel. Also add a field to indicate whether http.Get should be called or not:

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

Before running the test case add the test-case-specific channel to the map:

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

Then use the channel key field in the test case as part of the host's URL path:

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

And to check whether or not http.Get was called use select with some acceptable timeout:

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

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

huangapple
  • 本文由 发表于 2021年11月25日 03:21:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/70101818.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定