Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

why patches.Reset not effective? #145

Open
Zjmainstay opened this issue Nov 15, 2023 · 6 comments
Open

why patches.Reset not effective? #145

Zjmainstay opened this issue Nov 15, 2023 · 6 comments

Comments

@Zjmainstay
Copy link

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
Copy link

xhd2015 commented Mar 24, 2024

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

@qibingbuso
Copy link

+1. and idea now?

@Zjmainstay
Copy link
Author

Zjmainstay commented Aug 15, 2024

+1. and idea now?

try this

var wg sync.WaitGroup
for _, tt := range tests {
    t.Run(tt.name, func(t *testing.T) {
        wg.Add(1)
        go func() {
            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)
            }
            wg.Done()
        }()
        wg.Wait() //block to run one by one
    })
}

@jfossel2002
Copy link

Am seeing possible similar behavior, even we running out of parallel, go run gotest.tools/gotestsum@latest --junitfile report.xml --format testname -- -count=1 -p 1 ./... the tests still randomly fails every time (even though all work alone). When I add time.sleep(time.Sleep(100 * time.Millisecond)) to the end of each test, then all tests work.

@Zjmainstay
Copy link
Author

I found that adding runtime.GC() at the end of the t.Run function can also fix it. Like this:

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)
    }
    runtime.GC()
})

@kannae97
Copy link

Am seeing possible similar behavior, even we running out of parallel, go run gotest.tools/gotestsum@latest --junitfile report.xml --format testname -- -count=1 -p 1 ./... the tests still randomly fails every time (even though all work alone). When I add time.sleep(time.Sleep(100 * time.Millisecond)) to the end of each test, then all tests work.

Wow, your solution is the same as mine

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants