golang分层测试之http接口测试入门教程
前言
前几话主要讲解关于使用golang进行单元测试,在单元测试的上一层就是接口测试,本节主要讲使用golang进行接口测试,其中主要以http协议的接口测试来讲解
golang中的http请求
golang中拥有一个原生的http依赖库:net/http,http服务器的建立还是http客户端的开发,都会使用到这个依赖库,这里主要讲解时client部分,作为请求发起方应用于日常的接口测试,例示代码如下:
get请求
packagemain
import(
"fmt"
"io/ioutil"
"net/http"
)
funcmain(){
//模拟一个get提交请求
resp,err:=http.Get("http://127.0.0.1:12345/checkon")
iferr!=nil{
panic(err)
}
deferresp.Body.Close()//关闭连接
body,err:=ioutil.ReadAll(resp.Body)//读取body的内容
fmt.Println(string(body))
}
返回结果
E:\go_project>goruntestget.go
{
"code":200,
"data":"",
"msg":"online",
"state":"success"
}
post请求:
packagemain
import(
"fmt"
"io/ioutil"
"net/http"
"strings"
)
funcmain(){
//模拟一个post提交请求
resp,err:=http.Post("http://www.baidu.com","application/x-www-form-urlencoded",strings.NewReader("id=1"))
iferr!=nil{
panic(err)
}
//关闭连接
deferresp.Body.Close()
//读取报文中所有内容
body,err:=ioutil.ReadAll(resp.Body)
//输出内容
fmt.Println(string(body))
}
上面的post请求以form的方式,最后会返回一个页面
这里说明一下以下这行代码
deferresp.Body.Close()
首先是defer,Go的defer语句用来调度一个函数调用(被延期的函数),使其在执行defer的函数即将返回之前才被运行,被延期执行的函数,它的参数(包括接受者)实在defer执行的时候被求值的,而不是在调用执行的时候。也就是说被延期执行的函数的参数是按正常顺序被求值的,简单理解为,无论defer对应的代码行放在代码段的哪个位置,defer是在return前执行的代码行,但defer代码行中的参数是需要先声明再调用的,对应响应中的处理,golang的Response.Body需要被关闭的,body实际上是一个嵌套了多层的net.TCPConn:
- bufio.Reader,这层尝试将多次小的读操作替换为一次大的读操作,减少系统调用的次数,提高性能;
- io.LimitedReader,tcp连接在读取完body后不会关闭,继续读会导致阻塞,所以需要LimitedReader在body读完后发出eof终止读取;
- chunkedReader,解析chunked格式编码(如果不是chunked略过);
- bodyEOFSignal,在读到eof,或者是提前关闭body时会对readLoop发出回收连接的通知;
- gzipReader,解析gzip压缩(如果不是gizp压缩略过);
从上面可以看出如果body既没有被完全读取,也没有被关闭,那么这次http事务就没有完成,除非连接因超时终止了,否则相关资源无法被回收,所以需要我们进行关闭连接的操作,这个是很多golang新手会忽略的一个点,作为client端处理response的时候,body一定要close,否则会造成GC回收不到,继而产生内存泄露
带json的post请求
我们大部分应用到的restful接口都是用json格式的请求体,对应的golang的http请求也会有相关的方式postjson请求体
packagemain
import(
"fmt"
"io/ioutil"
"net/http"
"bytes"
"encoding/json"
)
typeHttpDatastruct{
Flagint`json:"flag"`
Msgstring`json:"msg"`
}
funcmain(){
url:="http://127.0.0.1:12345/postdata"
contentType:="application/json;charset=utf-8"
varhttpdataHttpData
httpdata.Flag=1
httpdata.Msg="terrychow"
b,err:=json.Marshal(httpdata)
iferr!=nil{
fmt.Println("jsonformaterror:",err)
return
}
body:=bytes.NewBuffer(b)
resp,err:=http.Post(url,contentType,body)
iferr!=nil{
fmt.Println("Postfailed:",err)
return
}
deferresp.Body.Close()
content,err:=ioutil.ReadAll(resp.Body)
iferr!=nil{
fmt.Println("Readfailed:",err)
return
}
fmt.Println("header:",resp.Header)
fmt.Println("content:",string(content))
}
执行结果响应
E:\go_project>gorungohttptest.go
header:map[Content-Type:[application/json]Content-Length:[78]Server:[Werkzeug/0.14.1Python/2.7.15]Date:[Thu,06Dec201816:35:11GMT]]
content:{
"code":200,
"data":1,
"msg":"terrychow",
"state":"success"
}
对于常用的get和post请求基本上就以照上面的版本执行,当然我们现在需要做的是http接口的测试,那就需要引入测试框架进行相关的校验,本文先讲解用之前提到的gocheck来进行断言
golang中的http接口测试
引入gocheck之后我们得到了以下的脚本:
packagehello_test
import(
"testing"
"fmt"
"strconv"
"io/ioutil"
"net/http"
"bytes"
"encoding/json"
."gopkg.in/check.v1"
)
varaint=1
//Hookupgocheckintothe"gotest"runner.
funcTest(t*testing.T){TestingT(t)}
typeMySuitestruct{}
typeHttpDatastruct{
Flagint`json:"flag"`
Msgstring`json:"msg"`
}
var_=Suite(&MySuite{})
vartesturlstring="http://127.0.0.1:12345"
func(s*MySuite)SetUpSuite(c*C){
str3:="第1次套件开始执行"
fmt.Println(str3)
//c.Skip("SkipTestSutie")
}
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)TestHttpGet(c*C){
geturl:=fmt.Sprintf("%v/checkon",testurl)
respget,err:=http.Get(geturl)
iferr!=nil{
panic(err)
}
deferrespget.Body.Close()//关闭连接
body,err:=ioutil.ReadAll(respget.Body)//读取body的内容
vargdatmap[string]interface{}//定义map用于解析resp.body的内容
iferr:=json.Unmarshal([]byte(string(body)),&gdat);err==nil{
fmt.Println(gdat)
}else{
fmt.Println(err)
}
vargmsg=gdat["msg"]
c.Assert(gmsg,Equals,"terrychow")//模拟失败的断言
}
func(s*MySuite)TestHttpPost(c*C){
url:=fmt.Sprintf("%v/postdata",testurl)
contentType:="application/json;charset=utf-8"
varhttpdataHttpData
httpdata.Flag=1
httpdata.Msg="terrychow"
b,err:=json.Marshal(httpdata)
iferr!=nil{
fmt.Println("jsonformaterror:",err)
return
}
body:=bytes.NewBuffer(b)
resp,err:=http.Post(url,contentType,body)
iferr!=nil{
fmt.Println("Postfailed:",err)
return
}
deferresp.Body.Close()
content,err:=ioutil.ReadAll(resp.Body)
iferr!=nil{
fmt.Println("Readfailed:",err)
return
}
vardatmap[string]interface{}//定义map用于解析resp.body的内容
iferr:=json.Unmarshal([]byte(string(content)),&dat);err==nil{
fmt.Println(dat)
}else{
fmt.Println(err)
}
varmsg=dat["msg"]
c.Assert(msg,Equals,"terrychow")//模拟成功的断言
}
最后的输出内容:
E:\go_project>gotest-vgocheckhttp_test.go ===RUNTest 第1次套件开始执行 第1条用例开始执行 map[code:200data:msg:onlinestate:success] 第1条用例执行完成 ---------------------------------------------------------------------- FAIL:gocheckhttp_test.go:56:MySuite.TestHttpGet gocheckhttp_test.go:72: c.Assert(gmsg,Equals,"terrychow") ...obtainedstring="online" ...expectedstring="terrychow" 第2条用例开始执行 map[msg:terrychowstate:successcode:200data:1] 第2条用例执行完成 第1次套件执行完成 OOPS:1passed,1FAILED ---FAIL:Test(0.02s) FAIL FAILcommand-line-arguments0.613s
输出的结果符合预期,这也是比较基本的http接口测试
小结
就上文来说,我们基本可以通过本文掌握如何做http接口测试,其核心还是使用http依赖库发出请求获取响应,利用gocheck进行断言,当然还可以用testing,下一节继续讲一下http接口测试,但会重点讲专门做http接口测试的测试框架httpexpect以及用于mock的httptest,希望对大家的学习有所帮助,也希望大家多多支持毛票票。