前言
- 前文主要讲解关于使用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 分层
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Big Java Late Objects
Horstmann, Cay S. / 2012-2 / 896.00元
The introductory programming course is difficult. Many students fail to succeed or have trouble in the course because they don't understand the material and do not practice programming sufficiently. ......一起来看看 《Big Java Late Objects》 这本书的介绍吧!