Golang mock 框架实践

版权声明:本文为博主原创文章,如果转载请注明,谢谢。 https://blog.csdn.net/baijiwei/article/details/82525964

Golang 有很多优秀的开源框架, 能够帮我们很方便的写出测试代码, 来模拟不同的场景。

gomonkey

gomonkey 是一种很简单的mock框架, 基于打桩函数的方式来进行mock不同的函数调用, 从而实现不同的场景的mock。该代码库地址: https://github.com/agiledragon/gomonkey

我们要测试一个函数, 而这个函数还调用了其他几个函数, 在测试代码中, 我们需要有代码调用上下文才能够使调用子函数成功, 但是通常, 调用上下文有可能很复杂, 没有办法将所有依赖的环境上下文配置好, 比如, 配置文件的设定, 数据库的设定, http/RPC的设定以及第三方库的设定。就是说, 变成上下文是如此的复杂, 几乎无法将所有的上下文配置好。

比较简单的办法就是避免的办法, 就是避免真是的子函数调用, 而是采用打桩的办法, 模拟子函数的调用, 返回不同的值来测试不同的场景。在后面我们给出一些例子来介绍具体的用法。

ApplyFunc 用法

import(
    "fmt"
)
func TestFunc(num1 int, num2 int) int {
     res := CompareInt(num1, num2)
     fmt.Println("CompareInt return ", res)

     return res
}     
func CompareInt(num1 int, num2 int) int {
    if num1 == num2 {
        return 0
    }else if num1 > num2 {
        return 1
    }else {
        return -1
    }
}

如下的代码, 采用ApplyFunc来测试CompareInt函数:

import (
    "fmt"
    . "github.com/agiledragon/gomonkey"
    . "github.com/smartystreets/goconvey/convey"
    "reflect"
    "testing"
)

func Test_CompareInt(t *testing.T) {
    Convey("CompareInttest", t, func() {
        // ApplyFunc 的第一个参数是待测试的函数名字;
        // 第二个参数是待测试函数的函数原型
        // 这里模拟返回值是0 的情况, 我们还可以返回1或者-1
        patch := ApplyFunc(CompareInt, func(_ int, _ int) (int) {
            return 0
        })
        result := TestFunc(1 1)
        So(result, ShouldEqual, 0)    
    })
    fmt.Println("CompareInt test success")
}

上面的例子是最简单情况, TestFunc调用函数CompareInt, 我们测试TestFunc的时候, 要先mock出来CompareInt, 采用ApplyFunc, 输入函数名字和待测试函数原型就可以打桩, 返回我们需要测试的场景。

ApplyMethod 用法

ApplyFunc对于稍微复杂一点的场景, 例如 interface{} 接口, 就不灵了, 还好gomonkey提供了另外一个函数ApplyMethod来实现这一场景。

import(
    "fmt"
)

type Person interface {
     Interesting(product string) bool
}

type Student strut {
}

func (s *Student) Interesting(prodName string) bool{
     if prodName == "pen" {
         return true
     } else {
         return false
      }
 }    

func testFunc(prodName string) bool{
    st := &Student{}
    result := st.Interesting(prodName)
    if false == result {
        fmt.Println("Student like product ", prodName)
    }
    return result
} 

ApplyMethod的用法和ApplyFunc的用法类似, 第一个参数代表struct的类型, 第二个参数是待测试函数的名字的字符串, 第三个参数是待测试函数的原型。

import (
    "fmt"
    . "github.com/agiledragon/gomonkey"
    . "github.com/smartystreets/goconvey/convey"
    "reflect"
    "testing"
)

func Test_testFunc(t *testing.T) {
    stu := &Student{}
    Convey("testFunc success", t, func() {
        patch := ApplyMethod(reflect.TypeOf(stu), "testFunc", func(_ string) (bool) {
            return true
        })
        result := TestFunc("pen")
        So(result, ShouldEqual, true)    
    })

    Convey("testFunc fail", t, func() {
        patch := ApplyMethod(reflect.TypeOf(stu), "testFunc", func(_ string) (bool) {
            return false
        })
        result := TestFunc("book")
        So(result, ShouldEqual, false)    
    })
    fmt.Println("CompareInt test success")
}

上面的例子, 函数testFunc传入一个字符串, 如果是“pen”就返回true, 否则false, 我们经过ApplyMethod来mock interface。

httptest

Http handler是golang代码里面很重要的一部分, 它是http调用的入口函数, 在golang的官方代码包里面包含了httptest, 它可以很方便的帮我们测试相关的代码。

import(
   "net/http"
   "encoding/json"
)

type Person struct {
   Name   string
   Age    int
}
http.HandleFunc("/get_host_info", GetHostInfoHandler)

func GetHostInfoHandler(w http.ResponseWriter, r *http.Request) {
    bytes, _:= ioutil.ReadAll(r.Body)
    if len(bytes) == 0 {
        w.WriteHeader(http.StatusBadRequest)
        return
    }

    request := &Person{}
    err = json.Unmarshal(bytes, &request)
    if err != nil {

        w.WriteHeader(http.StatusBadRequest)
        return
    }
    fmt.Println("Request paramter: %+v", request)
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-Type", "application/json")

    io.WriteString(w, `{"Code": 0, "Message":"Success"}`)
}

httptest主要就是mock出来一个http.Request, 以及一个http.ResponseWriter, 对于传入的参数以及返回给调用者的response, 可以根据所测试的函数进行适配。

import (
    "fmt"
    . "github.com/agiledragon/gomonkey"
    . "github.com/smartystreets/goconvey/convey"
    "reflect"
    "testing"
)
func Test_GetHostInfoHandler(t *testing.T) {
    reqData := &Person{
        Name: "baijiwei",
        Age:30,
    }

    reqBody, _ := json.Marshal(reqData)
    rw := httptest.NewRecorder()
    req, err := http.NewRequest("POST", "/get_host_info", bytes.NewReader(reqBody))
    if err != nil {                                                                                                              
         t.Fatal(err)
    }

    GetHostInfoHandler(rw, req)
    response := &Person{}
    err = json.Unmarshal(rw.Body.Bytes(), response)
    if err != nil {
         fmt.Println("unmarshal body fail")
    }
}

猜你喜欢

转载自blog.csdn.net/baijiwei/article/details/82525964