golang分层测试之http接口测试入门教程

前言

前几话主要讲解关于使用golang进行单元测试,在单元测试的上一层就是接口测试,本节主要讲使用golang进行接口测试,其中主要以http协议的接口测试来讲解

golang中的http请求

golang中拥有一个原生的http依赖库:net/http,http服务器的建立还是http客户端的开发,都会使用到这个依赖库,这里主要讲解时client部分,作为请求发起方应用于日常的接口测试,例示代码如下:

get请求

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package main

import (

 "fmt"

 "io/ioutil"

 "net/http"

)

func main() {

 //模拟一个get提交请求

 resp, err := http.Get("http://127.0.0.1:12345/checkon")

 if err != nil {

  panic(err)

 }

 defer resp.Body.Close() //关闭连接

 body, err := ioutil.ReadAll(resp.Body) //读取body的内容

 fmt.Println(string(body))

}

返回结果

1

2

3

4

5

6

7

E:\go_project>go run testget.go

{

 "code": 200,

 "data": "",

 "msg": "online",

 "state": "success"

}

post请求:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

package main

import (

 "fmt"

 "io/ioutil"

 "net/http"

 "strings"

)

func main() {

 //模拟一个post提交请求

 resp, err := http.Post("http://www.baidu.com", "application/x-www-form-urlencoded", strings.NewReader("id=1"))

 if err != nil {

  panic(err)

 }

 //关闭连接

 defer resp.Body.Close()

 //读取报文中所有内容

 body, err := ioutil.ReadAll(resp.Body)

 //输出内容

 fmt.Println(string(body))

}

上面的post请求以form的方式,最后会返回一个页面

这里说明一下以下这行代码

1

defer resp.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请求也会有相关的方式post json请求体

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

package main

import (

 "fmt"

 "io/ioutil"

 "net/http"

  "bytes"

 "encoding/json"

)

type HttpData struct {

 Flag int `json:"flag"`

 Msg string `json:"msg"`

}

func main() {

 url := "http://127.0.0.1:12345/postdata"

 contentType := "application/json;charset=utf-8"

 var httpdata HttpData

 httpdata.Flag = 1

 httpdata.Msg = "terrychow"

  

 b ,err := json.Marshal(httpdata)

 if err != nil {

  fmt.Println("json format error:", err)

  return

 }

 body := bytes.NewBuffer(b)

 resp, err := http.Post(url, contentType, body)

 if err != nil {

  fmt.Println("Post failed:", err)

  return

 }

 defer resp.Body.Close()

 content, err := ioutil.ReadAll(resp.Body)

 if err != nil {

  fmt.Println("Read failed:", err)

  return

 }

 fmt.Println("header:", resp.Header)

 fmt.Println("content:", string(content))

}

执行结果响应

1

2

3

4

5

6

7

8

E:\go_project>go run gohttptest.go

header: map[Content-Type:[application/json] Content-Length:[78] Server:[Werkzeug/0.14.1 Python/2.7.15] Date:[Thu, 06 Dec 2018 16:35:11 GMT]]

content: {

 "code": 200,

 "data": 1,

 "msg": "terrychow",

 "state": "success"

}

对于常用的get和post请求基本上就以照上面的版本执行,当然我们现在需要做的是http接口的测试,那就需要引入测试框架进行相关的校验,本文先讲解用之前提到的gocheck来进行断言

golang中的http接口测试

引入gocheck之后我们得到了以下的脚本:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

package hello_test

import (

 "testing"

 "fmt"

 "strconv"

 "io/ioutil"

 "net/http"

  "bytes"

 "encoding/json"

 . "gopkg.in/check.v1"

)

var a int =1

// Hook up gocheck into the "go test" runner.

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

type HttpData struct {

 Flag int `json:"flag"`

 Msg string `json:"msg"`

}

var _ = Suite(&MySuite{})

var testurl string ="http://127.0.0.1:12345"

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) TestHttpGet(c *C) {

 geturl := fmt.Sprintf("%v/checkon", testurl)

 respget, err := http.Get(geturl)

 if err != nil {

  panic(err)

 }

 defer respget.Body.Close() //关闭连接

 body, err := ioutil.ReadAll(respget.Body) //读取body的内容

 var gdat map[string]interface{} //定义map用于解析resp.body的内容

 if err := json.Unmarshal([]byte(string(body)), &gdat); err == nil {

  fmt.Println(gdat)

 } else {

  fmt.Println(err)

 }

 var gmsg=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"

 var httpdata HttpData

 httpdata.Flag = 1

 httpdata.Msg = "terrychow"

  

 b ,err := json.Marshal(httpdata)

 if err != nil {

  fmt.Println("json format error:", err)

  return

 }

 body := bytes.NewBuffer(b)

 resp, err := http.Post(url, contentType, body)

 if err != nil {

  fmt.Println("Post failed:", err)

  return

 }

 defer resp.Body.Close()

 content, err := ioutil.ReadAll(resp.Body)

 if err != nil {

  fmt.Println("Read failed:", err)

  return

 }

 var dat map[string]interface{} //定义map用于解析resp.body的内容

 if err := json.Unmarshal([]byte(string(content)), &dat); err == nil {

  fmt.Println(dat)

 } else {

  fmt.Println(err)

 }

 var msg=dat["msg"]

 c.Assert(msg, Equals, "terrychow") //模拟成功的断言

}

最后的输出内容:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

E:\go_project>go test -v gocheckhttp_test.go

=== RUN Test

第1次套件开始执行

第1条用例开始执行

map[code:200 data: msg:online state:success]

第1条用例执行完成

----------------------------------------------------------------------

FAIL: gocheckhttp_test.go:56: MySuite.TestHttpGet

gocheckhttp_test.go:72:

 c.Assert(gmsg, Equals, "terrychow")

... obtained string = "online"

... expected string = "terrychow"

第2条用例开始执行

map[msg:terrychow state:success code:200 data:1]

第2条用例执行完成

第1次套件执行完成

OOPS: 1 passed, 1 FAILED

--- FAIL: Test (0.02s)

FAIL

FAIL command-line-arguments 0.613s

输出的结果符合预期,这也是比较基本的http接口测试

小结

就上文来说,我们基本可以通过本文掌握如何做http接口测试,其核心还是使用http依赖库发出请求获取响应,利用gocheck进行断言,当然还可以用testing

​现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:485187702【暗号:csdn11】

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值