这一篇,我们来学习一下如何使用 httpmock 库,当使用 Go 语言编写 HTTP 服务时,可以使用第三方库来模拟 HTTP 请求和响应,例如:httpmock 库。这个库提供了一些方便的函数和结构体,用于在测试中模拟 HTTP 请求和验证期望的 HTTP 调用。
轻松模拟来自外部资源的 http 响应。
https://github.com/jarcoal/httpmock
01 服务端
package main
import (
"context"
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", index)
mux.Handle("/foo", http.HandlerFunc(foo))
mux.Handle("/bar", http.HandlerFunc(bar))
server := http.Server{
Addr: ":8081",
Handler: mux,
}
server.ListenAndServe()
}
func index(w http.ResponseWriter, r *http.Request) {
fmt.Println("index")
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write([]byte(`{"name": "index"}`))
}
func foo(w http.ResponseWriter, r *http.Request) {
fmt.Println("foo")
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write([]byte(`{"name": "foo"}`))
}
func bar(w http.ResponseWriter, r *http.Request) {
fmt.Println("bar")
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write([]byte(`{"name": "bar"}`))
}
func TestHttpGet(t *testing.T) {
// 对 foo 发送GET请求
resp, err := http.Get("http://localhost:8081/foo")
if err != nil {
t.Fatalf("Failed to send GET request: %v", err)
}
body, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
t.Log(string(body))
}
package main
import (
"github.com/jarcoal/httpmock"
"io/ioutil"
"net/http"
"testing"
)
func TestHttpMockGetRequest(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
expectedResponse := "Hello, World!"
httpmock.RegisterResponder("GET", "http://localhost:8081/foo",
httpmock.NewStringResponder(200, expectedResponse))
httpmock.RegisterResponder("GET", "http://localhost:8081/bar",
httpmock.NewStringResponder(200, expectedResponse))
// 对 foo 发送GET请求
resp, err := http.Get("http://localhost:8081/foo")
if err != nil {
t.Fatalf("Failed to send GET request: %v", err)
}
// 验证响应
body, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if string(body) != expectedResponse {
t.Errorf("Unexpected response. Expected: %s, Got: %s", expectedResponse, string(body))
}
// 对 bar 发送GET请求
resp, err = http.Get("http://localhost:8081/bar")
if err != nil {
t.Fatalf("Failed to send GET request: %v", err)
}
// 验证响应
body, _ = ioutil.ReadAll(resp.Body)
resp.Body.Close()
if string(body) != expectedResponse {
t.Errorf("Unexpected response. Expected: %s, Got: %s", expectedResponse, string(body))
}
count := httpmock.GetTotalCallCount()
t.Log(count)
}
func TestHttpMockRegexpRequest(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
httpmock.RegisterResponder("GET", `=~^http://localhost:8081/(\w+)\z`,
func(req *http.Request) (*http.Response, error) {
name := httpmock.MustGetSubmatch(req, 1) // 1=first regexp submatch
fmt.Println(name)
return httpmock.NewJsonResponse(200, map[string]interface{}{
"name": "My name is " + name,
})
})
resp, err := http.Get("http://localhost:8081/bar")
if err != nil {
t.Fatalf("Failed to send GET request: %v", err)
}
// 验证响应
body, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
t.Log(string(body))
}
package main
import (
"context"
"fmt"
"github.com/go-resty/resty/v2"
)
func Request(client *resty.Client) (*Result, error) {
url := "http://localhost:8081"
result := &Result{}
resp, err := client.SetTimeout(time.Second * 5).
R().SetContext(context.Background()).SetResult(result).Get(url)
// 出现网络等请求错误
if err != nil {
return nil, err
}
// 响应码不是 200
if resp.StatusCode() != http.StatusOK {
return nil, fmt.Errorf("status code: %d", resp.StatusCode())
}
return result, nil
}
func TestRequest(t *testing.T) {
client := resty.New()
method := "GET"
url := "http://localhost:8081"
convey.Convey("Test Request", t, func() {
// 1. 模拟出现网络错误
convey.Convey("模拟出现网络错误", func() {
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
httpmock.RegisterResponder(method, url, func(req *http.Request) (*http.Response, error) {
return nil, fmt.Errorf("network error")
})
_, err := Request(client)
convey.So(err, convey.ShouldNotBeNil)
})
// 2. 模拟出现500错误码
convey.Convey("模拟出现500错误码", func() {
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
httpmock.RegisterResponder(method, url, httpmock.NewStringResponder(500, ""))
_, err := Request(client)
convey.So(err, convey.ShouldNotBeNil)
})
// 3. 模拟正常请求
convey.Convey("模拟正常请求", func() {
httpmock.ActivateNonDefault(client.GetClient())
defer httpmock.DeactivateAndReset()
httpmock.RegisterResponder(method, url, func(req *http.Request) (*http.Response, error) {
return httpmock.NewJsonResponse(200, &Result{Name: "success"})
})
resp, err := Request(client)
convey.So(err, convey.ShouldBeNil)
convey.So(resp.Name, convey.ShouldEqual, "success")
})
})
}
httpmock.ActivateNonDefault(client.GetClient())
我们需要将 client 作为参数传入到 Request 方法中,才可以让 httpmock 去使用。
推荐阅读
欢迎关注 点赞 分享与收藏,一起学习进步!
专注于 Go 语言领域工作学习经验分享,期待与您一同学习进步。 学的不仅是技术,更是梦想!