h2non / gock

HTTP traffic mocking and testing made easy in Go ༼ʘ̚ل͜ʘ̚༽

Home Page:https://pkg.go.dev/github.com/h2non/gock

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to use Gock with Parallel tests

sazzer opened this issue · comments

I'll openly admit I'm not the most proficient Go user. As such, I make heavy use of tools like golangci-lint to validate my code.

One thing this constantly flags up is when I forget t.Parallel() in my tests. However, I've also noticed that if I am using this then my gock-based tests fail.

For example:

package application_test

import (
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gopkg.in/h2non/gock.v1"
)

func TestSimple(t *testing.T) {
	t.Parallel()

	defer gock.Off()

	gock.New("http://foo.com").
		Get("/bar").
		Reply(200)

	res, err := http.Get("http://foo.com/bar")

	require.NoError(t, err)
	assert.Equal(t, http.StatusOK, res.StatusCode)

	res.Body.Close()

	assert.True(t, gock.IsDone())
}

func TestSimple2(t *testing.T) {
	t.Parallel()

	defer gock.Off()

	gock.New("http://foo.com").
		Get("/bar").
		Reply(400)

	res, err := http.Get("http://foo.com/bar")

	require.NoError(t, err)
	assert.Equal(t, http.StatusBadRequest, res.StatusCode)

	res.Body.Close()

	assert.True(t, gock.IsDone())
}

This test file will sometimes pass, and will sometimes fail. And that's not surprising, because Gock has a bunch of static state that's shared between tests, and the tests are running in parallel.

Obviously removing t.Parallel() will make this work, but that seems a shame. I've also seen a suggestion of writing a TestMain() function that does all of the Gock work, but that also seems overly complicated for what's needed.

Are there any other recommended ways of doing tests like this?

Cheers

@sazzer, one workaround you can do is to create separate gock request instances for each test case and flush it off when the test case is executed.

eg.

type institution struct { ID string ShortName string }

type institutionResponse struct { Institutions []institution }

func NewTestGock() (*gock.Request, string) { baseURL := "https://localhost:63564" return gock.New(baseURL), baseURL }

`
func TestClient_Get(t *testing.T) {
t.Parallel()
expectedResp := institutionResponse{
Institutions: []institution{
{ID: "1033", ShortName: "CommBank"}},
}

type args struct {
	ctx  context.Context
	path string
}
tests := []struct {
	name    string
	args    args
	want    []byte
	wantErr bool
}{
	{
		name: "Success",
		args: args{
			ctx:  metadata.NewOutgoingContext(context.Background(), metadata.Pairs(interceptors.AuthMetadataKey, "token-value")),
			path: "v1/Finances/institutions",
		},
		want: func() []byte {
			data, _ := json.Marshal(expectedResp)
			return data
		}(),
		wantErr: false,
	},
	{
		name: "Unauthorized",
		args: args{
			ctx:  context.Background(),
			path: "v1/Finances/institutions",
		},
		want:    nil,
		wantErr: true,
	},
}
for _, tC := range tests {
	tt := tC
	t.Run(tt.name, func(t *testing.T) {
		t.Parallel()
		req, url := NewTestGock()
		defer gock.Remove(req.Mock)
		req.Get("v1/Finances/institutions").
			HeaderPresent("Authorization").
			Reply(200).
			JSON(expectedResp)
		c := coreapi.NewClient(&config.CoreAPI{
			BaseURL: url,
		})
		got, err := c.Get(tt.args.ctx, tt.args.path)
		if (err != nil) != tt.wantErr {
			t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
			return
		}
		if !reflect.DeepEqual(bytes.TrimRight(got, "\n"), bytes.TrimRight(tt.want, "\n")) {
			t.Errorf("Get() got = %v, want %v", got, tt.want)
		}
	})
}

}`

Doesn't work for me...

I just disabled the t.Parrallel() for the few cases I had...