前言
- 前文主要讲解关于使用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 分层
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Introduction to Linear Optimization
Dimitris Bertsimas、John N. Tsitsiklis / Athena Scientific / 1997-02-01 / USD 89.00
"The true merit of this book, however, lies in its pedagogical qualities which are so impressive..." "Throughout the book, the authors make serious efforts to give geometric and intuitive explanations......一起来看看 《Introduction to Linear Optimization》 这本书的介绍吧!