Golang httptest 的示例
当我第一次了解到它的 HTTP 测试包时,Go (Golang) 给我留下了深刻的印象。 在其他编程语言中,我习惯于做很多测试 HTTP 服务器的工作。 在 Go (Golang) 中,情况并非如此。 有一个很棒的测试包,可以从标准库中立即使用,而且非常容易理解。 让我们看看如何使用 httptest
包并为我们的 Go http 服务构建更好的端到端和 http 测试。
httptest
包背后的想法是很容易模拟 HTTP 服务器或模拟响应编写器以提供给我们的服务器处理程序。 这使得测试 http 服务器和客户端变得非常容易。
在本文中,我们将探索 httptest
包可以发挥作用的两个特定场景
- 测试 Go http 服务器处理程序
- 测试 Go http 客户端
测试 Go HTTP 服务器处理程序
让我们看一个 Go 中的 http 服务器的例子。 下面你可以找到一个服务器的例子,它接受一个词并返回它的大写版本。
package main
import (
"fmt"
"log"
"net/http"
"net/url"
"strings"
)
// Req: http://localhost:1234/upper?word=abc
// Res: ABC
func upperCaseHandler(w http.ResponseWriter, r *http.Request) {
query, err := url.ParseQuery(r.URL.RawQuery)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "invalid request")
return
}
word := query.Get("word")
if len(word) == 0 {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "missing word")
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, strings.ToUpper(word))
}
func main() {
http.HandleFunc("/upper", upperCaseHandler)
log.Fatal(http.ListenAndServe(":1234", nil))
}
在这种情况下,我们要测试用于我们的 Go 服务器的 upperCaseHandler
逻辑。 我们需要两件事来测试:
可以使用 httptest.NewRequest
导出函数创建的 http.Request
NewRequest
返回一个新的传入服务器 Request,适合传递给一个http.Handler
进行测试。
可以使用返回 httptest.ResponseRecorder
的 httptest.NewRecorder
类型创建的 http.ResponseWriter
ResponseRecorder
是http.ResponseWriter
的一个实现,它记录它的变化以供以后在测试中检查。
使用 httptest.ResponseRecorder
httptest.ResponseRecorder
是 http.ResponseWriter
的实现,可用于传递到我们的服务器处理程序,记录处理程序将写入响应的所有数据并返回之后写入的数据。 让我们看看它在下面的示例测试中是如何工作的。
package main
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
func TestUpperCaseHandler(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/upper?word=abc", nil)
w := httptest.NewRecorder()
upperCaseHandler(w, req)
res := w.Result()
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
if string(data) != "ABC" {
t.Errorf("expected ABC got %v", string(data))
}
}
这将允许我们通过检查 Result
方法返回的 ResponseRecorder
输出来测试处理程序响应
func (rw *ResponseRecorder) Result() *http.Response
结果返回处理程序生成的响应。 返回的 Response 将至少填充其 StatusCode、Header、Body 和可选的 Trailer。 将来可能会填充更多字段,因此调用者不应在测试中使用 DeepEqual 结果。
测试 Go HTTP 客户端
测试 Go 服务器处理程序相对容易,尤其是当您只想测试处理程序逻辑时。 您只需要在测试中模拟 http.ResponseWriter
和 http.Request
对象。 但是,当使用 Go http 客户端时,事情会稍微复杂一些。 原因是有时模拟或复制整个服务器并不那么容易。 让我们看看下面的例子。
package main
import (
"io/ioutil"
"net/http"
"github.com/pkg/errors"
)
type Client struct {
url string
}
func NewClient(url string) Client {
return Client{url}
}
func (c Client) UpperCase(word string) (string, error) {
res, err := http.Get(c.url + "/upper?word=" + word)
if err != nil {
return "", errors.Wrap(err, "unable to complete Get request")
}
defer res.Body.Close()
out, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", errors.Wrap(err, "unable to read response data")
}
return string(out), nil
}
我们的 Go 客户端需要一个代表远程服务器基本 URL 的基本 URL。 然后它使用给定的输入词调用方法 /upper
并将结果作为字符串返回给调用者,或者在请求未成功的情况下返回错误。 为了测试这段代码,我们需要模拟整个服务器逻辑,或者至少模拟负责给定请求路径的逻辑,比如我们的例子中的 /upper
路径。 使用 httptest 包,我们可以通过初始化一个监听环回接口的本地服务器来模拟整个服务器,该接口将返回我们想要的任何东西。
使用 httptest.Server
我们要使用的类型是 httptest.Server
类型,通过调用 httptest.NewServer
函数。
服务器是一个 HTTP 服务器,在本地环回接口上侦听系统选择的端口,用于端到端 HTTP 测试。
func NewServer(handler http.Handler) *Server
NewServer
启动并返回一个新的 Server。 调用者应在完成后调用 Close 以将其关闭。
让我们看看我们如何在下面的例子中做到这一点。
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestClientUpperCase(t *testing.T) {
expected := "dummy data"
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, expected)
}))
defer svr.Close()
c := NewClient(svr.URL)
res, err := c.UpperCase("anything")
if err != nil {
t.Errorf("expected err to be nil got %v", err)
}
// res: expected\r\n
// due to the http protocol cleanup response
res = strings.TrimSpace(res)
if res != expected {
t.Errorf("expected res to be %s got %s", expected, res)
}
}
在上面的示例中,我们刚刚使用 httptest.NewServer
函数创建了一个新的模拟服务器,为其分配了一个始终返回相同数据的自定义模拟处理程序,并将服务器 URL 用作我们客户端的服务器 URL。 这允许我们模拟并指示服务器返回我们想要用于测试目的的任何东西。
相关文章
在 Golang 中使用 If-Else 和 Switch Loop Inside HTML 模板
发布时间:2023/04/27 浏览次数:65 分类:Go
-
本篇文章介绍了在 Golang 的 HTML 模板中使用 if-else 和 switch 循环。因此,只要输出是 HTML,就应该始终使用 HTML 模板包而不是文本模板。
Golang 中的零值 Nil
发布时间:2023/04/27 浏览次数:166 分类:Go
-
本篇文章介绍 nil 在 Golang 中的含义,nil 是 Go 编程语言中的零值,是众所周知且重要的预定义标识符。
Golang 中的 Lambda 表达式
发布时间:2023/04/27 浏览次数:93 分类:Go
-
本篇文章介绍如何在 Golang 中创建 lambda 表达式。Lambda 表达式似乎不存在于 Golang 中。 函数文字、lambda 函数或闭包是匿名函数的另一个名称。
在 Go 中使用断言
发布时间:2023/04/27 浏览次数:181 分类:Go
-
本篇文章介绍了 assert 在 GoLang 中的使用。在 Go 语言中使用断言:GoLang 不提供对断言的任何内置支持,但我们可以使用来自 Testify API 的广泛使用的第三方包断言。
Go 中的随机数生成
发布时间:2023/04/27 浏览次数:114 分类:Go
-
本篇文章介绍如何在 Go 语言中使用随机数生成功能。Go 中的随机数生成 Go 语言为随机数生成功能提供内置支持。 内置包 math 有方法 rand(),用于随机数生成。
在 Go 中使用 Electron API 创建 GUI
发布时间:2023/04/27 浏览次数:124 分类:Go
-
本篇文章介绍如何在 Go 语言中使用 Electron API 创建 GUI。Electron API 或 Astilectron 用于为 GoLang 创建 GUI。
在 GoLang 中安装包
发布时间:2023/04/27 浏览次数:122 分类:Go
-
使用 Go 语言的 get 命令安装所需的包非常容易。 Go 语言提供了多种命令来执行某些任务,get 就是其中之一。