golang Layered Testing tutorial for http Interface testing

  • 2020-06-23 00:36:12
  • OfStack

preface

The first few sentences are mainly about using golang for unit testing. The first layer of unit testing is interface testing. This section is mainly about using golang for interface testing, which is mainly about using http protocol interface testing

http request in golang

There is a native http dependency library in golang: net/http, which will be used by the establishment of http server or the development of http client. Here, the client part is mainly explained as the initiator of the request and applied to daily interface test. The example code is as follows:

get request


package main

import (
 "fmt"
 "io/ioutil"
 "net/http"
)

func main() {
 // simulation 1 a get Submit a request 
 resp, err := http.Get("http://127.0.0.1:12345/checkon")
 if err != nil {
  panic(err)
 }
 defer resp.Body.Close() // Close the connection 
 body, err := ioutil.ReadAll(resp.Body) // read body The content of the 
 fmt.Println(string(body))
}

Returns the result


E:\go_project>go run testget.go
{
 "code": 200,
 "data": "",
 "msg": "online",
 "state": "success"
}

post request:


package main

import (
 "fmt"
 "io/ioutil"
 "net/http"
 "strings"
)

func main() {
 // simulation 1 a post Submit a request 
 resp, err := http.Post("http://www.baidu.com", "application/x-www-form-urlencoded", strings.NewReader("id=1"))
 if err != nil {
  panic(err)
 }
 // Close the connection 
 defer resp.Body.Close()
 // Read all contents of the message 
 body, err := ioutil.ReadAll(resp.Body)
 // output 
 fmt.Println(string(body))
}

The post request above is in form fashion and returns 1 page

Here's the following line of code


defer resp.Body.Close()

The first is defer. The defer statement of Go is used to schedule a function call (deferred) to be run before the return of the function that executed defer. The deferred function whose arguments (including the receiver) are evaluated at defer execution, rather than at call execution. That is to be extended to perform the function of the parameter is in normal order is evaluated, the simple understanding for, whether defer corresponding lines of code in the code segment which position, defer is performed before return lines of code, but defer the parameters in the line of code is need statement first, then call, corresponding to the response of the processing, golang Response. Body need to be closed, body had, in fact, a nested layers of net. TCPConn:

bufio. Reader, this layer tries to replace multiple small read operations with one big read operation to reduce the number of system calls and improve performance; The connection of ES67en.LimitedReader and tcp will not close after reading body. Continuing reading will cause blocking. Therefore, LimitedReader should issue eof to terminate reading after reading body. chunkedReader, parsing chunked format encoding (if not chunked skipping); bodyEOFSignal, when reading eof, or closing body in advance, will send a notification to recall the connection to readLoop; gzipReader, parsing gzip compression (if not gizp compression omitted);

Can be seen from the above if body was neither read completely, has not been closed, so the http transaction is not completed, unless the connection because of a timeout, the related resources can't be recycled, so we need to close the connection operation, this is many novice golang ignore 1 point, as dealing with response client end, body1 must close, otherwise it will cause GC recycling, which in turn generate memory leaks

post request with json

Most of the restful interfaces we use are request bodies in json format, and the corresponding http request bodies in post format also have related ways


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))

}

Execute result response


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"
}

The common get and post requests are basically executed as above. Of course, what we need to do now is to test the http interface, so we need to introduce the test framework for verification. This article first explains how to use the gocheck mentioned before to make assertions

http interface tests in golang

After introducing gocheck, we got the following script:


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:=" The first 1 The second suite starts execution "
 fmt.Println(str3)
 //c.Skip("Skip TestSutie")
}

func (s *MySuite) TearDownSuite(c *C) {
 str4:=" The first 1 The sub-suite execution is complete "
 fmt.Println(str4)
}

func (s *MySuite) SetUpTest(c *C) {
 str1:=" The first "+strconv.Itoa(a)+" The use case begins execution "
 fmt.Println(str1)

}

func (s *MySuite) TearDownTest(c *C) {
 str2:=" The first "+strconv.Itoa(a)+" Bar use case execution completes "
 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() // Close the connection 

 body, err := ioutil.ReadAll(respget.Body) // read body The content of the 
 var gdat map[string]interface{} // define map Used to resolve resp.body The content of the 
 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") // Simulate failed assertions 

}

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{} // define map Used to resolve resp.body The content of the 
 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") // Simulate successful assertions 
}

The final output content:


E:\go_project>go test -v gocheckhttp_test.go
=== RUN Test
 The first 1 The second suite starts execution 
 The first 1 The use case begins execution 
map[code:200 data: msg:online state:success]
 The first 1 Bar use case execution completes 

----------------------------------------------------------------------
FAIL: gocheckhttp_test.go:56: MySuite.TestHttpGet

gocheckhttp_test.go:72:
 c.Assert(gmsg, Equals, "terrychow")
... obtained string = "online"
... expected string = "terrychow"

 The first 2 The use case begins execution 
map[msg:terrychow state:success code:200 data:1]
 The first 2 Bar use case execution completes 
 The first 1 The sub-suite execution is complete 
OOPS: 1 passed, 1 FAILED
--- FAIL: Test (0.02s)
FAIL
FAIL command-line-arguments 0.613s

The output is as expected, and this is the basic http interface test

summary


Related articles: