Search code examples
gogo-http

field state of web.Request struct not cloning properly in Golang


In trying to set up two different web.Request states for use in some test cases, one without any headers and one with, I run into issue:

Setup

I create fakeRequest,fakeRequestNoHeaders thus:

// create fake request
fakeRequest := new(web.Request)
fakeRequest.Request = httptest.NewRequest("GET",
    fakeServer.URL,
    nil)

fakeRequestNoHeaders := new(web.Request)
fakeRequestNoHeaders.Request = fakeRequest.Request

// give fakeRequest some headers
fakeRequest.Header.Add("Authorization", "Bearer ksjaf;oipyu7")
fakeRequest.Header.Add("Scope", "test")

Sanity Test

I expect, of course, that fakeRequest.Header != fakeRequestNoHeaders.Header. I write that test:

t.Run("HeadersSanityTest", func(t *testing.T) {


    assert.NotEqualf(t,
        fakeRequest.Header,
        fakeRequestNoHeaders.Header,
        "fakeRequest,fakeRequestNoHeaders share the same header state")

Result of test

It fails.

Why is this and how can I achieve what I'm trying?

UPDATE: I found the culprit:

the underlying http.Request, returned by httptest.NewRequest, is actually a pointer. Header simply belongs to that Request. The problem now reduces down to "How to deep-copy that Request."


Solution

  • The issue was, indeed, not with the Header field, but instead the Request field, which was a pointer. (Oh no! I accidentally shallow-copied)

    The Solution

    I recalled, in one of my earlier tests, a method that I wrote specifically to get around this:

    func makeBasicRequest() *web.Request {
        baseReq := httptest.NewRequest(http.MethodPost,
            "[some url here]",
            nil)
        req := new(web.Request)
        req.Request = baseReq
        return req
    }
    

    I basically just brought it into this test, and used it, hitting it once per fake request that I needed.