如何使用httprouter编写测试

I am writing tests for a simple REST service in GoLang. But, because I am using julienschmidt/httprouter as the routing library. I am struggling on how to write test.

main.go

package main

func main() {
   router := httprouter.New()
   bookController := controllers.NewBookController()
   router.GET("/book/:id", bookController.GetBook)
   http.ListenAndServe(":8080", router)
}

controllers

package controllers

type BookController struct {}

func NewBookController *BookController {
   return &BookController()
}

func (bc BookController) GetBook(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
   fmt.Fprintf(w,"%s", p)
}

My question is: How can test this while GetBook is neither HttpHandler nor HttpHandle

If I use a traditional handler, the test will be easy like this

func TestGetBook(t *testing.T) {
  req, _ := http.NewRequest("GET", "/book/sampleid", nil)
  rr := httptest.NewRecorder()
  handler := controllers.NewBookController().GetBook
  handler.ServeHTTP(rr,req)
  if status := rr.code; status != http.StatusOK {
    t.Errorf("Wrong status")
  }
}

The problem is, httprouter is not handler, or handlefunc. So I am stuck now

Just spin up a new router for each test and then register the handler under test, then pass the test request to the router, not the handler, so that the router can parse the path parameters and pass them to the handler.

func TestGetBook(t *testing.T) {
    handler := controllers.NewBookController()
    router := httprouter.New()
    router.GET("/book/:id", handler.GetBook)

    req, _ := http.NewRequest("GET", "/book/sampleid", nil)
    rr := httptest.NewRecorder()

    router.ServeHTTP(rr, req)
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("Wrong status")
    }
}

You need to wrap your handler so that it can be accessed as an http.HandlerFunc:

func TestGetBook(t *testing.T) {
    req, _ := http.NewRequest("GET", "/book/sampleid", nil)
    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        controllers.NewBookController().GetBook(w, r, httprouter.Params{})
    })
    handler.ServeHTTP(rr,req)
    if status := rr.code; status != http.StatusOK {
        t.Errorf("Wrong status")
    }
}

If your handler requires parameters, you'll either have to parse them manually from the request, or just supply them as the third argument.

u can try this without ServeHTTP

func TestGetBook(t *testing.T) {
    req := httptest.NewRequest("GET", "http://example.com/foo", nil)
    w := httptest.NewRecorder()

    controllers.NewBookController().GetBook(w, req, []httprouter.Param{{Key: "id", Value: "101"}})

    resp := w.Result()
    body, _ := ioutil.ReadAll(resp.Body)

    t.Log(resp.StatusCode)
    t.Log(resp.Header.Get("Content-Type"))
    t.Log(string(body))
}