Golang httptest包測(cè)試使用教程
當(dāng)前首次學(xué)習(xí)到Golang httptest包時(shí),著實(shí)打動(dòng)了我。其他語(yǔ)言測(cè)試HTTP服務(wù)需要做很多工作或引用第三方工具,讓人不可思議的是,Golang標(biāo)準(zhǔn)庫(kù)就提供了非常容易理解的測(cè)試包。本文介紹httptest包的使用,為你Go http服務(wù)構(gòu)建更好的端到端的測(cè)試。
httptest包的理念是,非常容易模擬http服務(wù),也就是說(shuō)模擬響應(yīng)寫(response writer),提供給http處理器(handle),讓我們測(cè)試http服務(wù)端和客戶端很容易。
本文主要介紹兩個(gè)使用httptest的特定場(chǎng)景: 測(cè)試http server處理器,測(cè)試http客戶端。
測(cè)試http服務(wù)端處理器
下面通過(guò)示例介紹http server的測(cè)試。首先看http服務(wù)程序,把請(qǐng)求字符串轉(zhuǎn)為大寫:
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)) }
現(xiàn)在想測(cè)試http server使用的upperCaseHandler邏輯,我們需要準(zhǔn)備兩方面:
- 使用httptest.NewRequest暴露的函數(shù)創(chuàng)建http.Request對(duì)象,NewRequest返回Request, 可以傳給http.Handler進(jìn)行測(cè)試.
- 使用httptest.NewRecorder函數(shù)創(chuàng)建http.ResponseWriter,返回httptest.ResponseRecorder。ResponseRecorder是
http.ResponseWriter 的實(shí)現(xiàn),它記錄變化為了后面測(cè)試檢查.
httptest.ResponseRecorder
httptest.ResponseRecorder是 http.ResponseWriter 的實(shí)現(xiàn),可以傳給http server handle,記錄所有處理并寫回響應(yīng)的數(shù)據(jù),下面測(cè)試程序可以看到其如何實(shí)現(xiàn):
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)) } }
上面示例中首先定義請(qǐng)求和響應(yīng),然后傳入處理器進(jìn)行測(cè)試。然后檢查ResponseRecorder的Result方法輸出:
func (rw *ResponseRecorder) Result() *http.Response
Result返回處理器生成的響應(yīng)。返回相應(yīng)至少有StatusCode, Header, Body, 以及可選其他內(nèi)容,未來(lái)可能會(huì)填充更多字段,所以調(diào)用者在測(cè)試中不應(yīng)該深度比較相等。
測(cè)試HTTP客戶端
測(cè)試服務(wù)端處理器相對(duì)容易,特別當(dāng)測(cè)試處理器邏輯時(shí),僅需要在測(cè)試中模擬http.ResponseWriter 和 http.Request對(duì)象。對(duì)于HTTP客戶端測(cè)試,情況稍晚有點(diǎn)復(fù)雜。原因是有時(shí)不容易模擬或復(fù)制整個(gè)HTTP Server,請(qǐng)看下面示例:
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 }
client需要url,表示遠(yuǎn)程服務(wù)端基地址。然后調(diào)用/upper
,帶上輸入單詞,最后返回結(jié)果字符串給調(diào)用者,如果調(diào)用不成功還返回錯(cuò)誤對(duì)象。為了測(cè)試這段代碼,需要模擬整個(gè)http服務(wù)端邏輯,或至少是響應(yīng)請(qǐng)求路徑:/upper。使用httptest包可以模擬整個(gè)http 服務(wù),通過(guò)初始化本地服務(wù),監(jiān)聽(tīng)回環(huán)地址并返回你想要的任何內(nèi)容。
使用 httptest.Server
通過(guò)調(diào)用httptest.NewServer函數(shù)生成我們想要的 httptest.Server。表示http服務(wù),監(jiān)聽(tīng)回環(huán)地址及可選的端口號(hào),用于實(shí)現(xiàn)端到端HTTP測(cè)試。
func NewServer(handler http.Handler) *Server
NewServer 啟動(dòng)并返回新的HTTP服務(wù),調(diào)用者使用完成后應(yīng)該調(diào)用Close方法結(jié)束服務(wù)。下面通過(guò)示例進(jìn)行解釋:
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函數(shù)創(chuàng)建了模擬http服務(wù)器,給它傳入自定義模擬處理器,總是返回相同的數(shù)據(jù)。并使用服務(wù)端url作為客戶端請(qǐng)求url,從而模擬并讓服務(wù)端返回任何我們想測(cè)試的內(nèi)容。
當(dāng)然我們可以修改處理器,讓其返回我們期望的邏輯:
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 response res = strings.TrimSpace(res) if res != expected { t.Errorf("expected res to be %s got %s", expected, res) } }
總結(jié)
本文介紹httptest包,可以很方便測(cè)試http服務(wù)端處理邏輯,以及模擬http服務(wù)端測(cè)試客戶端請(qǐng)求邏輯。由于很方面模擬,從而可以把一組參數(shù)和期望值進(jìn)行組合,循環(huán)進(jìn)行測(cè)試并對(duì)比結(jié)果,可以極大地提升測(cè)試效率。
到此這篇關(guān)于Golang httptest包測(cè)試使用教程的文章就介紹到這了,更多相關(guān)Go httptest內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang實(shí)現(xiàn)將中文轉(zhuǎn)化為拼音
這篇文章主要為大家詳細(xì)介紹了如何通過(guò)Golang實(shí)現(xiàn)將中文轉(zhuǎn)化為拼音功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-02-02Go語(yǔ)言中的定時(shí)器原理與實(shí)戰(zhàn)應(yīng)用
在Go語(yǔ)言中,Timer和Ticker是處理定時(shí)任務(wù)的重要工具,Timer用于一次性事件,而Ticker則用于周期性事件,本文詳細(xì)介紹了這兩種定時(shí)器的創(chuàng)建、使用和停止方法,并通過(guò)實(shí)際案例展示了它們?cè)诒O(jiān)控日志、檢查系統(tǒng)狀態(tài)等方面的應(yīng)用2024-10-10利用Go語(yǔ)言實(shí)現(xiàn)在終端繪制小兔子
這篇文章主要為大家詳細(xì)介紹了如何利用Go語(yǔ)言實(shí)現(xiàn)在終端繪制小兔子來(lái)給大家拜個(gè)早年,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2023-01-01Go并發(fā)讀寫文件、分片寫、分片下載文件的實(shí)現(xiàn)示例
讀寫文件在很多項(xiàng)目中都可以用到,本文主要介紹了Go并發(fā)讀寫文件、分片寫、分片下載文件的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01Golang設(shè)計(jì)模式工廠模式實(shí)戰(zhàn)寫法示例詳解
這篇文章主要為大家介紹了Golang 工廠模式實(shí)戰(zhàn)寫法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08ubuntu下搭建Go語(yǔ)言(golang)環(huán)境
這篇文章主要介紹了ubuntu下搭建Go語(yǔ)言(golang)環(huán)境,需要的朋友可以參考下2015-01-01Go語(yǔ)言編程中判斷文件是否存在是創(chuàng)建目錄的方法
這篇文章主要介紹了Go語(yǔ)言編程中判斷文件是否存在是創(chuàng)建目錄的方法,示例都是使用os包下的函數(shù),需要的朋友可以參考下2015-10-10