前言
- 前文主要讲解关于使用golang的http+gocheck进行接口测试,测试的流程基本上都是请求,返回数据和检验数据,但按照上文基于gocheck的实现显示还不太完善,今天主要讲解一下如果利用httpexpect进行http的接口测试,以及在前后端分离的研发过程中,server端未实现时使用httptest作为mock服务器的应用过程
httpexpect
- 先讲一下httpexpect,httpexpect是专门用于做http rest api接口测试的,轻量级的测试框架。其代码格式写法有些类似于node.js的mocha, 用go get获取
go get -u github.com/gavv/httpexpect
- 安装完成之后既可以导入并编写测试代码
- 示例代码
package example import ( "net/http" "testing" "github.com/gavv/httpexpect" ) var testurl string ="http://127.0.0.1:12345" func TestHttpGetPass(t *testing.T) { e:= httpexpect.New(t, testurl) //创建一个httpexpect实例 e.GET("/checkon"). //ge请求 Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg"). //检验是否包括key ValueEqual("msg","online") //对比key的value } func TestHttpGetFail(t *testing.T) { e:= httpexpect.New(t, testurl) //创建一个httpexpect实例 e.GET("/checkon"). //ge请求 Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg1"). //检验是否包括key,使用不存在的key ValueEqual("msg","online") //对比key的value } func TestHttpPostPass(t *testing.T) { e:= httpexpect.New(t, testurl) //创建一个httpexpect实例 postdata:= map[string]interface{}{ //创建一个json变量 "flag": 1, "msg":"terrychow", } contentType := "application/json;charset=utf-8" e.POST("/postdata"). //post 请求 WithHeader("ContentType", contentType). //定义头信息 WithJSON(postdata). //传入json body Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg"). //检验是否包括key ValueEqual("msg","terrychow") //对比key的value } func TestHttpPostFail(t *testing.T) { e:= httpexpect.New(t, testurl) //创建一个httpexpect实例 postdata:= map[string]interface{}{ //创建一个json变量 "flag": 1, "msg":"terrychow", } contentType := "application/json;charset=utf-8" e.POST("/postdata"). //post 请求 WithHeader("ContentType", contentType). //定义头信息 WithJSON(postdata). //传入json body Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg"). //检验是否包括key ValueEqual("msg","terryzhou") //对比key的value,value不匹配 }
- 运行结果如下:
E:\go_project>go test -v httpexpect_test.go === RUN TestHttpGetPass --- PASS: TestHttpGetPass (0.01s) printer.go:25: GET http://127.0.0.1:12345/checkon === RUN TestHttpGetFail --- FAIL: TestHttpGetFail (0.00s) printer.go:25: GET http://127.0.0.1:12345/checkon reporter.go:23: Error Trace: reporter.go:23 chain.go:21 object.go:162 httpexpect_test.go:37 Error: expected object containing key 'msg1', but got: { "code": 200, "data": "", "msg": "online", "state": "success" } Test: TestHttpGetFail === RUN TestHttpPostPass --- PASS: TestHttpPostPass (0.01s) printer.go:25: POST http://127.0.0.1:12345/postdata === RUN TestHttpPostFail --- FAIL: TestHttpPostFail (0.00s) printer.go:25: POST http://127.0.0.1:12345/postdata reporter.go:23: Error Trace: reporter.go:23 chain.go:21 object.go:255 httpexpect_test.go:83 Error: expected value for key 'msg' equal to: "terryzhou" but got: "terrychow" diff: (unavailable) Test: TestHttpPostFail FAIL FAIL command-line-arguments 0.568s
- httpexpect用于测试的api非常丰富,包括过程中的json化,获取body字段等都比之前直接用http+gocheck要方便很多,当然也可以利用gocheck把这个测试的过程串联起来,形成标准化的测试用例集
package example import ( "net/http" "testing" "fmt" "strconv" "github.com/gavv/httpexpect" . "gopkg.in/check.v1" ) var a int =1 type MySuite struct{} var testurl string ="http://127.0.0.1:12345" var _ = Suite(&MySuite{}) func Test(t *testing.T) { TestingT(t) } func (s *MySuite) SetUpSuite(c *C) { str3:="第1次套件开始执行" fmt.Println(str3) //c.Skip("Skip TestSutie") } func (s *MySuite) TearDownSuite(c *C) { str4:="第1次套件执行完成" fmt.Println(str4) } func (s *MySuite) SetUpTest(c *C) { str1:="第"+strconv.Itoa(a)+"条用例开始执行" fmt.Println(str1) } func (s *MySuite) TearDownTest(c *C) { str2:="第"+strconv.Itoa(a)+"条用例执行完成" fmt.Println(str2) a=a+1 } func (s *MySuite) TestHttpGetPass(c *C) { //使用check实例代替testing实例即可,check实例本身就是继承testing实例 e:= httpexpect.New(c, testurl) //创建一个httpexpect实例 //传入替换成check实例 e.GET("/checkon"). //ge请求 Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg"). //检验是否包括key ValueEqual("msg","online") //对比key的value } func (s *MySuite) TestHttpGetFail(c *C) { e:= httpexpect.New(c, testurl) //创建一个httpexpect实例 e.GET("/checkon"). //ge请求 Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg1"). //检验是否包括key,使用不存在的key ValueEqual("msg","online") //对比key的value } func (s *MySuite) TestHttpPostPass(c *C) { e:= httpexpect.New(c, testurl) //创建一个httpexpect实例 postdata:= map[string]interface{}{ //创建一个json变量 "flag": 1, "msg":"terrychow", } contentType := "application/json;charset=utf-8" e.POST("/postdata"). //post 请求 WithHeader("ContentType", contentType). //定义头信息 WithJSON(postdata). //传入json body Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg"). //检验是否包括key ValueEqual("msg","terrychow") //对比key的value } func (s *MySuite) TestHttpPostFail(c *C) { e:= httpexpect.New(c, testurl) //创建一个httpexpect实例 postdata:= map[string]interface{}{ //创建一个json变量 "flag": 1, "msg":"terrychow", } contentType := "application/json;charset=utf-8" e.POST("/postdata"). //post 请求 WithHeader("ContentType", contentType). //定义头信息 WithJSON(postdata). //传入json body Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg"). //检验是否包括key ValueEqual("msg","terryzhou") //对比key的value,value不匹配 }
- 运行结果
$ go test expect_test.go 第1次套件开始执行 第1条用例开始执行 第1条用例执行完成 ---------------------------------------------------------------------- FAIL: expect_test.go:58: MySuite.TestHttpGetFail GET http://127.0.0.1:12345/checkon expect_test.go:65: e.GET("/checkon"). //ge请求 Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg1"). //检验是否包括key,使用不存在的key ValueEqual("msg", "online") //对比key的value /Users/zhouweiqu/mygo/src/github.com/stretchr/testify/assert/assertions.go:260: t.Errorf("\n%s", ""+labeledOutput(content...)) ... Error: Error Trace: reporter.go:23 chain.go:21 object.go:162 expect_test.go:65 Error: expected object containing key 'msg1', but got: { "code": 200, "data": "", "msg": "online", "state": "success" } 第2条用例开始执行 第2条用例执行完成 第3条用例开始执行 第3条用例执行完成 ---------------------------------------------------------------------- FAIL: expect_test.go:89: MySuite.TestHttpPostFail POST http://127.0.0.1:12345/postdata expect_test.go:105: e.POST("/postdata"). //post 请求 WithHeader("ContentType", contentType). //定义头信息 WithJSON(postdata). //传入json body Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("msg"). //检验是否包括key ValueEqual("msg", "terryzhou") //对比key的value,value不匹配 /Users/zhouweiqu/mygo/src/github.com/stretchr/testify/assert/assertions.go:260: t.Errorf("\n%s", ""+labeledOutput(content...)) ... Error: Error Trace: reporter.go:23 chain.go:21 object.go:255 expect_test.go:105 Error: expected value for key 'msg' equal to: "terryzhou" but got: "terrychow" diff: (unavailable) 第4条用例开始执行 第4条用例执行完成 第1次套件执行完成 OOPS: 2 passed, 2 FAILED --- FAIL: Test (0.01s) FAIL FAIL command-line-arguments 0.037s
- 建议初入门的同学可以使用本文的httpexpect+gocheck的方式做基本的接口测试,会比较简单入手,同时也好维护
httptest
- httptest是golang自有的标准mock server依赖库,如前文所说,前后端分离的研发过程中,server端未实现时需要利用到mock,httptest就提供给我们这个功能,使用它不用真正的启动一个http server或者请求任意的http server
- 假设现在有个场景,就是要获取学生的信息接口,由于一些问题服务器不可用,这时候就可以使用httptest来模拟响应
- 被测接口代码:student.go
package student import ( "encoding/json" "fmt" "io/ioutil" "net/http" ) const ( NAME = "terrychow" ) type Student struct { Name string `json:"name"` Sex string `json:"sex"` Class string `json:"clase"` Number string `json:"number"` } func GetStudentInfo(api string) ([]Student, error) { //将要被mock的接口 url := fmt.Sprintf("%s/student?name=%s", api, NAME) resp, err := http.Get(url) if err != nil { return []Student{}, err } if resp.StatusCode != http.StatusOK { return []Student{}, fmt.Errorf("Resp is didn't 200 OK:%s", resp.Status) } bodybytes, _ := ioutil.ReadAll(resp.Body) personList := make([]Student, 0) err = json.Unmarshal(bodybytes, &personList) if err != nil { fmt.Errorf("Decode data fail") return []Student{}, fmt.Errorf("Decode data fail") } return personList, nil
- mock服务器代码:student_test.go
package student import ( "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" ) var StudentResp = []Student{ { Name: "terrychow", Sex: "m", Class: "001", Number: "00101", }, { Name: "terryzhou", Sex: "m", Class: "001", Number: "00102", }, { Name: "lilizhou", Sex: "w", Class: "002", Number: "00101", }, } var StudentRespBytes, _ = json.Marshal(StudentResp) func TestGetInfoFail(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) w.Write(StudentRespBytes) if r.Method != "GET" { t.Errorf("Except 'Get' got '%s'", r.Method) } if r.URL.EscapedPath() != "/student" { t.Errorf("Except to path '/student',got '%s'", r.URL.EscapedPath()) } r.ParseForm() stuname := r.Form.Get("name") if stuname!= "terrychow" { t.Errorf("Except rquest to have 'name=terrychow',got '%s'", stuname) } })) defer ts.Close() api := ts.URL fmt.Printf("Url:%s\n", api) resp, err := GetStudentInfo(api) if err != nil { fmt.Println("ERR:", err) } else { fmt.Println("resp:", resp) } } func TestGetInfoOK(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write(StudentRespBytes) if r.Method != "GET" { t.Errorf("Except 'Get' got '%s'", r.Method) } if r.URL.EscapedPath() != "/student" { t.Errorf("Except to path '/student',got '%s'", r.URL.EscapedPath()) } r.ParseForm() stuname := r.Form.Get("name") if stuname!= "terrychow" { t.Errorf("Except rquest to have 'name=terrychow',got '%s'", stuname) } })) defer ts.Close() api := ts.URL fmt.Printf("Url:%s\n", api) resp, err := GetStudentInfo(api) if err != nil { fmt.Println("ERR:", err) } else { fmt.Println("resp:", resp) } }
- 运行结果通过
go test student_test.go student.go ok command-line-arguments (cached)
- 假设调整一下里面的student.go的请求值
NAME = "terry"
- 运行结果:
go test student_test.go student.go Url:http://127.0.0.1:60989 ERR: Resp is didn't 200 OK:401 Unauthorized --- FAIL: TestGetInfoFail (0.00s) student_test.go:48: Except rquest to have 'name=terrychow',got 'terry' Url:http://127.0.0.1:60991 resp: [{terrychow m 001 00101} {terryzhou m 001 00102} {lilizhou w 002 00101}] --- FAIL: TestGetInfoOK (0.00s) student_test.go:78: Except rquest to have 'name=terrychow',got 'terry' FAIL FAIL command-line-arguments 0.021s
-
这样就可以看到这个mock的过程,当然在mock服务器建立之前,我们为了可以检验一下mock是否可以用,可以使用httpexpect来检验
使用
package student import ( "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/gavv/httpexpect" . "gopkg.in/check.v1" ) type MySuite struct{} var _ = Suite(&MySuite{}) func Test(t *testing.T) { TestingT(t) } var StudentResp= map[string]interface{}{ //创建一个json变量 "Name": "terrychow", "Sex": "m", "Class": "001", "Number": "00101", } var StudentRespBytes, _ = json.Marshal(StudentResp) func (s *MySuite) TestGetInfoFail(c *C) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(StudentRespBytes) })) defer ts.Close() e := httpexpect.New(c, ts.URL) e.GET("/student?name=terrychow"). Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("Name"). //检验是否包括key ValueEqual("Name","terryzhou") //对比key的value } func (s *MySuite) TestGetInfoOK(c *C) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write(StudentRespBytes) })) defer ts.Close() e := httpexpect.New(c, ts.URL) e.GET("/student?name=terrychow"). Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("Name"). //检验是否包括key ValueEqual("Name","terrychow") //对比key的value }
- 其运行结果
go test student_test.go ---------------------------------------------------------------------- FAIL: student_test.go:30: MySuite.TestGetInfoFail GET http://127.0.0.1:63777/student%3Fname=terrychow student_test.go:45: e.GET("/student?name=terrychow"). Expect(). Status(http.StatusOK). //判断请求是否200 JSON(). Object(). //json body实例化 ContainsKey("Name"). //检验是否包括key ValueEqual("Name", "terryzhou") //对比key的value /Users/zhouweiqu/mygo/src/github.com/stretchr/testify/assert/assertions.go:260: t.Errorf("\n%s", ""+labeledOutput(content...)) ... Error: Error Trace: reporter.go:23 chain.go:21 object.go:255 student_test.go:45 Error: expected value for key 'Name' equal to: "terryzhou" but got: "terrychow" diff: (unavailable) OOPS: 1 passed, 1 FAILED --- FAIL: Test (0.01s) FAIL FAIL command-line-arguments 0.024s
- 这样httptest的mock server就能够检查是可用的
总结
- 接口测试相关的篇章基本讲述完毕,通过这几篇文章希望大家对使用golang进行http接口测试有所了解,接下来就会开始将本次专题文章的重点,使用golang进行压测,希望大家继续关注
以上所述就是小编给大家介绍的《golang分层测试之httpexpect+httptest测试应用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- golang分层测试之单元测试-testing使用
- golang分层测试之单元测试-gocheck使用
- golang分层测试之http接口测试入门
- golang分层测试之语言入门
- 说说分层测试中HTTP的API测试工具
- DDD 分层
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
学习JavaScript数据结构与算法
[巴西] 格罗纳(Loiane Groner) / 孙晓博、邓钢、吴双、陈迪、袁源 / 人民邮电出版社 / 2015-10-1 / 39.00
本书首先介绍了JavaScript语言的基础知识,接下来讨论了数组、栈、队列、链表、集合、字典、散列表、树、图等数据结构,之后探讨了各种排序和搜索算法,包括冒泡排序、选择排序、插入排序、归并排序、快速排序、顺序搜索、二分搜索,还介绍了动态规划和贪心算法等常用的高级算法及相关知识。一起来看看 《学习JavaScript数据结构与算法》 这本书的介绍吧!