I'm working on a simple toy API in Golang using httprouter
and trying to write tests for my endpoints. One of my endpoints is a "show movie" endpoint with a path like /v1/movies/:id
. For this endpoint, I initially wrote the test creating the request like `
req := httptest.NewRequest(http.MethodGet, "/v1/movies/"+tc.id,
but for some reason this wasn't enough. After a lot of googling, it turns out I have to do something like the below, between the //**** comments, where the params are manually added to the request context. I'm confused why this is necessary, since shouldn't the handler receive the ID in the path the way I wrote it above? This isn't necessary when writing regular client code (I think). Looking for an explanation on why this is necessary, if it's only for testing purposes, or what. TIA
func TestShowMovieHandler(t *testing.T) {
app := &application{}
testCases := []struct {
name string
id string
expectedStatus int
expectedBody string
}{
{
name: "Valid ID",
id: "1",
expectedStatus: http.StatusOK,
expectedBody: "getting movie 1\n",
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Create a request with the specified ID
req := httptest.NewRequest(http.MethodGet, "/v1/movies/"+tc.id, nil)
rr := httptest.NewRecorder()
//**** why is this necessary
params := httprouter.Params{httprouter.Param{Key: "id", Value: tc.id}}
t.Logf("%#v\n", httprouter.ParamsKey)
t.Logf("%#v\n", params)
ctx := context.WithValue(req.Context(), httprouter.ParamsKey, params)
req = req.WithContext(ctx)
//****
app.showMovieHandler(rr, req)
if rr.Code != tc.expectedStatus {
t.Errorf("expected status %v; got %v", tc.expectedStatus, rr.Code)
}
if rr.Body.String() != tc.expectedBody {
t.Errorf("expected body %q; got %q", tc.expectedBody, rr.Body.String())
}
})
}
}
I was able to find a solution but seeking explanation why it works
The router processes the path parameters. The test does not use the router. Fix by using a router.
t.Run(tc.name, func(t *testing.T) {
// Create a request with the specified ID
req := httptest.NewRequest(http.MethodGet, "/v1/movies/"+tc.id, nil)
rr := httptest.NewRecorder()
router := httprouter.New()
router.HandlerFunc("GET", "/v1/movies/:id", app.showMovieHandler)
router.ServeHTTP(rr, req)
if rr.Code != tc.expectedStatus {
t.Errorf("expected status %v; got %v", tc.expectedStatus, rr.Code)
}
if rr.Body.String() != tc.expectedBody {
t.Errorf("expected body %q; got %q", tc.expectedBody, rr.Body.String())
}
})