Golang httptest实战教程
创始人
2024-06-03 11:28:02
0

当前首次学习到Golang httptest包时,着实打动了我。其他语言测试HTTP服务需要做很多工作或引用第三方工具,让人不可思议的是,Golang标准库就提供了非常容易理解的测试包。本文介绍httptest包的使用,为你Go http服务构建更好的端到端的测试。

httptest包的理念是,非常容易模拟http服务,也就是说模拟响应写(response writer),提供给http处理器(handle),让我们测试http服务端和客户端很容易。

本文主要介绍两个使用httptest的特定场景: 测试http server处理器,测试http客户端。

测试http服务端处理器

下面通过示例介绍http server的测试。首先看http服务程序,把请求字符串转为大写:

package mainimport ("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))
}

现在想测试http server使用的upperCaseHandler逻辑,我们需要准备两方面:

  • 使用httptest.NewRequest暴露的函数创建http.Request对象,NewRequest返回Request, 可以传给http.Handler进行测试.

  • 使用httptest.NewRecorder函数创建http.ResponseWriter,返回httptest.ResponseRecorder。ResponseRecorder是
    http.ResponseWriter 的实现,它记录变化为了后面测试检查.

httptest.ResponseRecorder

httptest.ResponseRecorder是 http.ResponseWriter 的实现,可以传给http server handle,记录所有处理并写回响应的数据,下面测试程序可以看到其如何实现:

package mainimport ("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))}
}

上面示例中首先定义请求和响应,然后传入处理器进行测试。然后检查ResponseRecorder的Result方法输出:

func (rw *ResponseRecorder) Result() *http.Response

Result返回处理器生成的响应。返回相应至少有StatusCode, Header, Body, 以及可选其他内容,未来可能会填充更多字段,所以调用者在测试中不应该深度比较相等。

测试HTTP客户端

测试服务端处理器相对容易,特别当测试处理器逻辑时,仅需要在测试中模拟http.ResponseWriter 和 http.Request对象。对于HTTP客户端测试,情况稍晚有点复杂。原因是有时不容易模拟或复制整个HTTP Server,请看下面示例:

package mainimport ("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
}

client需要url,表示远程服务端基地址。然后调用/upper,带上输入单词,最后返回结果字符串给调用者,如果调用不成功还返回错误对象。为了测试这段代码,需要模拟整个http服务端逻辑,或至少是响应请求路径:/upper。使用httptest包可以模拟整个http 服务,通过初始化本地服务,监听回环地址并返回你想要的任何内容。

使用 httptest.Server

  • 通过调用httptest.NewServer函数生成我们想要的 httptest.Server。表示http服务,监听回环地址及可选的端口号,用于实现端到端HTTP测试。
func NewServer(handler http.Handler) *Server

NewServer 启动并返回新的HTTP服务,调用者使用完成后应该调用Close方法结束服务。下面通过示例进行解释:

package mainimport ("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 responseres = strings.TrimSpace(res)if res != expected {t.Errorf("expected res to be %s got %s", expected, res)}
}

上面示例中使用httptest.NewServer函数创建了模拟http服务器,给它传入自定义模拟处理器,总是返回相同的数据。并使用服务端url作为客户端请求url,从而模拟并让服务端返回任何我们想测试的内容。

当然我们可以修改处理器,让其返回我们期望的逻辑:


func TestClientUpperCase(t *testing.T) {svr := httptest.NewServer(http.HandlerFunc(func(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 {fmt.Fprintf(w, strings.ToUpper(word))} else {fmt.Fprintf(w, "no input")}}))defer svr.Close()expected := "ANYTHING"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 responseres = strings.TrimSpace(res)if res != expected {t.Errorf("expected res to be %s got %s", expected, res)}
}

总结

本文介绍httptest包,可以很方便测试http服务端处理逻辑,以及模拟http服务端测试客户端请求逻辑。由于很方面模拟,从而可以把一组参数和期望值进行组合,循环进行测试并对比结果,可以极大地提升测试效率。

上一篇:Python中的Requests库

下一篇:四等分list

相关内容

热门资讯

电视安卓系统哪个品牌好,哪家品... 你有没有想过,家里的电视是不是该升级换代了呢?现在市面上电视品牌琳琅满目,各种操作系统也是让人眼花缭...
安卓会员管理系统怎么用,提升服... 你有没有想过,手机里那些你爱不释手的APP,背后其实有个强大的会员管理系统在默默支持呢?没错,就是那...
安卓系统软件使用技巧,解锁软件... 你有没有发现,用安卓手机的时候,总有一些小技巧能让你玩得更溜?别小看了这些小细节,它们可是能让你的手...
安卓系统提示音替换 你知道吗?手机里那个时不时响起的提示音,有时候真的能让人心情大好,有时候又让人抓狂不已。今天,就让我...
安卓开机不了系统更新 手机突然开不了机,系统更新还卡在那里,这可真是让人头疼的问题啊!你是不是也遇到了这种情况?别急,今天...
安卓系统中微信视频,安卓系统下... 你有没有发现,现在用手机聊天,视频通话简直成了标配!尤其是咱们安卓系统的小伙伴们,微信视频功能更是用...
安卓系统是服务器,服务器端的智... 你知道吗?在科技的世界里,安卓系统可是个超级明星呢!它不仅仅是个手机操作系统,竟然还能成为服务器的得...
pc电脑安卓系统下载软件,轻松... 你有没有想过,你的PC电脑上安装了安卓系统,是不是瞬间觉得世界都大不一样了呢?没错,就是那种“一机在...
电影院购票系统安卓,便捷观影新... 你有没有想过,在繁忙的生活中,一部好电影就像是一剂强心针,能瞬间让你放松心情?而我今天要和你分享的,...
安卓系统可以写程序? 你有没有想过,安卓系统竟然也能写程序呢?没错,你没听错!这个我们日常使用的智能手机操作系统,竟然有着...
安卓系统架构书籍推荐,权威书籍... 你有没有想过,想要深入了解安卓系统架构,却不知道从何下手?别急,今天我就要给你推荐几本超级实用的书籍...
安卓系统看到的炸弹,技术解析与... 安卓系统看到的炸弹——揭秘手机中的隐形威胁在数字化时代,智能手机已经成为我们生活中不可或缺的一部分。...
鸿蒙系统有安卓文件,畅享多平台... 你知道吗?最近在科技圈里,有个大新闻可是闹得沸沸扬扬的,那就是鸿蒙系统竟然有了安卓文件!是不是觉得有...
宝马安卓车机系统切换,驾驭未来... 你有没有发现,现在的汽车越来越智能了?尤其是那些豪华品牌,比如宝马,它们的内饰里那个大屏幕,简直就像...
p30退回安卓系统 你有没有听说最近P30的用户们都在忙活一件大事?没错,就是他们的手机要退回安卓系统啦!这可不是一个简...
oppoa57安卓原生系统,原... 你有没有发现,最近OPPO A57这款手机在安卓原生系统上的表现真是让人眼前一亮呢?今天,就让我带你...
安卓系统输入法联想,安卓系统输... 你有没有发现,手机上的输入法真的是个神奇的小助手呢?尤其是安卓系统的输入法,简直就是智能生活的点睛之...
怎么进入安卓刷机系统,安卓刷机... 亲爱的手机控们,你是否曾对安卓手机的刷机系统充满好奇?想要解锁手机潜能,体验全新的系统魅力?别急,今...
安卓系统程序有病毒 你知道吗?在这个数字化时代,手机已经成了我们生活中不可或缺的好伙伴。但是,你知道吗?即使是安卓系统,...
奥迪中控安卓系统下载,畅享智能... 你有没有发现,现在汽车的中控系统越来越智能了?尤其是奥迪这种豪华品牌,他们的中控系统简直就是科技与艺...