为什么这个 GoLang Mock HTTP 响应器返回了错误的调用次数?

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

Why does this GoLang Mock HTTP responder return the wrong number of times it was called?

问题

我正在为我的Go应用程序编写测试用例,该应用程序进行HTTP请求。为了模拟来自远程主机的响应,我创建了这个stringProducer类。

type stringProducer struct {
    strings   []string
    callCount int
}

func (s *stringProducer) GetNext() string {
    if s.callCount >= len(s.strings) {
        panic("ran out of responses")
    }
    s.callCount++
    fmt.Println("s.CallCount =", s.callCount)
    return s.strings[s.callCount-1]
}

func mockHTTPResponder(producer stringProducer) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(producer.GetNext()))
    })
}

以下是我在main函数中调用它的方式:

func main() {
    producer := stringProducer{
        strings: []string{"Hello World!"},
    }

    srv := httptest.NewServer(mockHTTPResponder(producer))
    if producer.callCount != 0 {
        panic("callCount is not 0")
    }

    var buf io.ReadWriter
    req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf("%s/path/to/something", srv.URL), buf)

    newClient := http.Client{}

    newClient.Do(req)

    if producer.callCount != 1 {
        panic("callCount is not 1")
    }
}

在这段代码中,当发起HTTP请求时,它会发送到上面的响应器,响应器会返回预先指定的文本。它还会将计数器stringProducer.callCount增加1。

从下面的程序输出中,你可以看到它打印了一行显示callCount增加到1的信息。然而,当我检查相同的值时,它不是1,而是0。为什么?如何修复这个问题?

s.CallCount = 1
panic: callCount is not 1

goroutine 1 [running]:
main.main()
    /tmp/sandbox3935766212/prog.go:50 +0x118

Go Playground链接在这里:https://play.golang.org/p/mkiJAfrMdCw

英文:

I'm writing test cases for my Go application that makes HTTP requests.
To simulate a response from the remote host, I have created this class stringProducer

type stringProducer struct {
	strings   []string
	callCount int
}

func (s *stringProducer) GetNext() string {
	if s.callCount >= len(s.strings) {
		panic("ran out of responses")
	}
	s.callCount++
	fmt.Println("s.CallCount = ", s.callCount)
	return s.strings[s.callCount-1]
}

func mockHTTPResponder(producer stringProducer) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(producer.GetNext()))
	})
}

Here is how I call it in my main function:

func main() {
	producer := stringProducer{
		strings: []string{"Hello World!"},
	}

	srv := httptest.NewServer(mockHTTPResponder(producer))
	if producer.callCount != 0 {
		panic("callCount is not 0")
	}

	var buf io.ReadWriter
	req, _ := http.NewRequestWithContext(context.Background(), http.MethodGet, fmt.Sprintf("%s/path/to/something", srv.URL), buf)

	newClient := http.Client{}

	newClient.Do(req)

	if producer.callCount != 1 {
		panic("callCount is not 1")
	}
}

In this code, when an HTTP request is made, it goes to the responder above which responds with some pre-specified text. It also causes the counter stringProducer.callCount to be incremented by 1.

From the program's output below, you can see that it prints a line showing that callCount was incremented to 1. However, when I check that same value, it is not 1. It is zero. Why? And how to fix that?

s.CallCount =  1
panic: callCount is not 1

goroutine 1 [running]:
main.main()
    /tmp/sandbox3935766212/prog.go:50 +0x118

Go Playground link here: https://play.golang.org/p/mkiJAfrMdCw

答案1

得分: 2

你在mockHTTPResponder中通过值传递了stringProducer。这样做会得到mockHTTPResponder内部变量的副本。所有后续的更改都是在该副本上进行的(原始的stringProducer保持不变):

func mockHTTPResponder(producer stringProducer) http.Handler { // <- producer是原始变量的副本
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(producer.GetNext()))  // <- 在副本上进行s.callCount++操作
    })
}

mockHTTPResponder中传递一个指针。

英文:

You pass by value stringProducer in mockHTTPResponder. When you do this you get a copy of the variable inside mockHTTPResponder. And all the following changes are made on that copy (original stringProducer are left unchanged):

func mockHTTPResponder(producer stringProducer) http.Handler { // &lt;- producer is a copy of the original variable
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(producer.GetNext()))  // &lt;- s.callCount++ on the copy
    })
}

Pass a pointer inside mockHTTPResponder.

huangapple
  • 本文由 发表于 2021年8月28日 16:54:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/68962808.html
匿名

发表评论

匿名网友

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

确定