golang分层测试之httpexpect+httptest测试应用

栏目: Go · 发布时间: 5年前

前言

  • 前文主要讲解关于使用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测试应用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Chinese Authoritarianism in the Information Age

Chinese Authoritarianism in the Information Age

Routledge / 2018-2-13 / GBP 115.00

This book examines information and public opinion control by the authoritarian state in response to popular access to information and upgraded political communication channels among the citizens in co......一起来看看 《Chinese Authoritarianism in the Information Age》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具