How to unit test a Go Gin handler function?

To test operations that involve the HTTP request, you have to actually initialize an *http.Request and set it to the Gin context. To specifically test c.BindQuery it’s enough to properly initialize the request’s URL and URL.RawQuery:

func mockGin() (*gin.Context, *httptest.ResponseRecorder) {
    w := httptest.NewRecorder()
    c, _ := gin.CreateTestContext(w)

    // test request, must instantiate a request first
    req := &http.Request{
        URL:    &url.URL{},
        Header: make(http.Header), // if you need to test headers
    }
    // example: req.Header.Add("Accept", "application/json")

    // request query
    testQuery := weldprogs.QueryParam{/* init fields */}

    q := req.URL.Query()
    for _, s := range testQuery.Basematgroup_id {
        q.Add("basematgroup_id", s)
    }
    // ... repeat for other fields as needed

    // must set this, since under the hood c.BindQuery calls
    // `req.URL.Query()`, which calls `ParseQuery(u.RawQuery)`
    req.URL.RawQuery = q.Encode()
    
    // finally set the request to the gin context
    c.Request = req

    return c, w
}

If you need to mock JSON binding, see this answer.


The service call services.WeldprogService.GetMaterialByFilter(&queryParam) can’t be tested as is. To be testable it has to be (ideally) an interface and somehow injected as dependency of your handler.

Assuming that it is already an interface, to make it injectable, you either require it as an handler argument — but this forces you to change the signature of the handler —, or you set it as a Gin context value:

func GetMaterialByFilter(c *gin.Context) {
    //...
    weldprogService := mustGetService(c)
    materialByFilter, getErr := weldprogService.GetMaterialByFilter(&queryParam)
    // ...
}

func mustGetService(c *gin.Context) services.WeldprogService {
    svc, exists := c.Get("svc_context_key")
    if !exists {
        panic("service was not set")
    }
    return svc.(services.WeldprogService)
}

Then you can mock it in your unit tests:

type mockSvc struct {
}

// have 'mockSvc' implement the interface 

func TestGetMaterialByFilter(t *testing.T) {
    w := httptest.NewRecorder()
    c, _ := gin.CreateTestContext(w)

    // now you can set mockSvc into the test context
    c.Set("svc_context_key", &mockSvc{})

    GetMaterialByFilter(c)
    // ... 
}

Leave a Comment