agiledragon / gomonkey

gomonkey is a library to make monkey patching in unit tests easy
MIT License
1.92k stars 178 forks source link

why patches.Reset not effective? #145

Open Zjmainstay opened 7 months ago

Zjmainstay commented 7 months ago

Behaviors:

  1. Only one test case can run normally every time.
  2. Multiple test cases lead to different random results.

file1: http_request.go

package test

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

func GetIp(url string) string {
    // 发送GET请求
    response, err := http.Get(url)
    if err != nil {
        fmt.Printf("请求发生错误: %s\n", err)
        return ""
    }
    defer response.Body.Close()

    // 读取响应数据
    body, err := io.ReadAll(response.Body)
    fmt.Println("ioReadAll", string(body), err)
    if err != nil {
        fmt.Printf("读取响应数据发生错误: %s\n", err)
        return ""
    }

    return string(body)
}

file2:http_request_test.go

package test

import (
    "bytes"
    "errors"
    "fmt"
    "io"
    "net/http"
    "reflect"
    "testing"

    "github.com/agiledragon/gomonkey/v2"
)

func TestGetIp(t *testing.T) {
    tests := []struct {
        name            string
        url             string
        mockHttpGetFunc func(url string) (resp *http.Response, err error)
        mockReadAllFunc func(r io.Reader) ([]byte, error)
        expectIp        string
        expectErr       bool
    }{
        {
            name: "正常获取IP信息",
            url:  "http://valid.url",
            mockHttpGetFunc: func(url string) (resp *http.Response, err error) {
                return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString("192.168.0.1"))}, nil
            },
            mockReadAllFunc: func(r io.Reader) ([]byte, error) {
                return []byte("192.168.0.1"), nil
            },
            expectIp: "192.168.0.1",
        },
        {
            name: "http.Get错误",
            url:  "http://invalid.url",
            mockHttpGetFunc: func(url string) (resp *http.Response, err error) {
                return nil, errors.New("http get error")
            },
        },
        {
            name: "io.ReadAll错误",
            url:  "http://valid.url",
            mockHttpGetFunc: func(url string) (resp *http.Response, err error) {
                return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString("192.168.0.2"))}, nil
            },
            mockReadAllFunc: func(r io.Reader) ([]byte, error) {
                return nil, errors.New("read all error")
            },
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            patches := gomonkey.NewPatches()
            defer patches.Reset()
            fmt.Println(tt.name, "tt.mockHttpGetFunc is nil:", tt.mockHttpGetFunc == nil)
            fmt.Println(tt.name, "tt.mockReadAllFunc is nil:", tt.mockReadAllFunc == nil)
            patches.ApplyFunc(io.ReadAll, tt.mockReadAllFunc)
            patches.ApplyFunc(http.Get, tt.mockHttpGetFunc)

            ip := GetIp(tt.url)

            if !reflect.DeepEqual(ip, tt.expectIp) {
                t.Errorf("GetIp() = %v, expect %v", ip, tt.expectIp)
            }
        })
    }
}

test command

go test -gcflags=all=-l

result unexpect

正常获取IP信息 tt.mockHttpGetFunc is nil: false
正常获取IP信息 tt.mockReadAllFunc is nil: false
ioReadAll 192.168.0.1 <nil>
http.Get错误 tt.mockHttpGetFunc is nil: false
http.Get错误 tt.mockReadAllFunc is nil: true
请求发生错误: http get error
io.ReadAll错误 tt.mockHttpGetFunc is nil: false
io.ReadAll错误 tt.mockReadAllFunc is nil: false
ioReadAll 192.168.0.1 <nil>
--- FAIL: TestGetIp (0.00s)
    --- FAIL: TestGetIp/io.ReadAll错误 (0.00s)
        http_request_test.go:66: GetIp() = 192.168.0.1, expect 
FAIL
exit status 1
FAIL    godemo/test/http_request        0.005s

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

正常获取IP信息 tt.mockHttpGetFunc is nil: false
正常获取IP信息 tt.mockReadAllFunc is nil: false
ioReadAll 192.168.0.1 <nil>
http.Get错误 tt.mockHttpGetFunc is nil: false
http.Get错误 tt.mockReadAllFunc is nil: true
ioReadAll 192.168.0.1 <nil>
io.ReadAll错误 tt.mockHttpGetFunc is nil: false
io.ReadAll错误 tt.mockReadAllFunc is nil: false
ioReadAll 192.168.0.1 <nil>
--- FAIL: TestGetIp (0.00s)
    --- FAIL: TestGetIp/http.Get错误 (0.00s)
        http_request_test.go:66: GetIp() = 192.168.0.1, expect 
    --- FAIL: TestGetIp/io.ReadAll错误 (0.00s)
        http_request_test.go:66: GetIp() = 192.168.0.1, expect 
FAIL
exit status 1
FAIL    godemo/test/http_request        0.006s

result expect

正常获取IP信息 tt.mockHttpGetFunc is nil: false
正常获取IP信息 tt.mockReadAllFunc is nil: false
ioReadAll 192.168.0.1 <nil>
http.Get错误 tt.mockHttpGetFunc is nil: false
http.Get错误 tt.mockReadAllFunc is nil: true
请求发生错误: http get error
io.ReadAll错误 tt.mockHttpGetFunc is nil: false
io.ReadAll错误 tt.mockReadAllFunc is nil: false
ioReadAll  read all error
读取响应数据发生错误: read all error
PASS
ok      godemo/test/http_request        0.005s
xhd2015 commented 3 months ago

Note that gomonkey is not concurrent safe. If you need that, can try https://github.com/xhd2015/xgo