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...