0

I am trying to write unit test for my http file server. I have implemented the ServeHTTP function so that it'd replace "//" with "/" in the URL:

type slashFix struct {
    mux http.Handler
}

func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    r.URL.Path = strings.Replace(r.URL.Path, "//", "/", -1)
    h.mux.ServeHTTP(w, r)
}

The bare-minimum code would look like this:

func StartFileServer() {
    httpMux := http.NewServeMux()
    httpMux.HandleFunc("/abc/", basicAuth(handle))
    http.ListenAndServe(":8000", &slashFix{httpMux})
}

func handle(writer http.ResponseWriter, r *http.Request) {
    dirName := "C:\\Users\\gayr\\GolandProjects\\src\\NDAC\\download\\"    
    http.StripPrefix("/abc",
        http.FileServer(http.Dir(dirName))).ServeHTTP(writer, r)
}

func basicAuth(handler http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        user, pass, ok := r.BasicAuth()
        if user != "UserName" || pass != "Password" {
            w.WriteHeader(401)
            w.Write([]byte("Unauthorised.\n"))
            return
        }
        handler(w, r)
    }
}

I came across instances like the following to test http handlers:

req, err := http.NewRequest("GET", "/abc/testfile.txt", nil)
if err != nil {
    t.Fatal(err)
}
req.SetBasicAuth("UserName", "Password")
rr := httptest.NewRecorder()
handler := http.HandlerFunc(basicAuth(handle))
handler.ServeHTTP(rr, req)

Doing so would invoke the ServeHTTP function implemented using http.HandleFunc, but I want ServeHTTP implemented in my code to be invoked. How can this be achieved? Also, is there a way for me to directly test StartFileServer()?

Edit: I checked the link provided in the comments; my question does not appear to be a duplicate. I have a specific question: instead of invoking the ServeHTTP function implemented using http.HandleFunc, I want ServeHTTP implemented in my code to be invoked. I do not see this addressed in the provided link.

9
  • Possible duplicate of How to test http calls in go using httptest Commented Jun 21, 2018 at 9:27
  • I checked the link provided above; my question does not appear to be a duplicate. I have specific question: instead of invoking the ServeHTTP function implemented using http.HandleFunc, I want ServeHTTP implemented in my code to be invoked. Commented Jun 21, 2018 at 9:40
  • 1
    That is answered. Just pass an instance of your slashFix type, as it's already an http.Handler. Commented Jun 21, 2018 at 9:42
  • Passing the instance of slashFix (following this answer: stackoverflow.com/a/37549527/5772695) calls ServeHTTP implemented by me, but how do I incorporate it with basicAuth(handle) which basically does the file serving part? Without this, I get "panic: runtime error: invalid memory address or nil pointer dereference" Commented Jun 21, 2018 at 9:58
  • basicAuth should take an http.Handler rather than http.HandlerFunc, then it will be easy. Commented Jun 21, 2018 at 10:23

1 Answer 1

2

http.HandlerFunc implements http.Handler. As Flimzy pointed out in the comments, there is no need for basicAuth to require a HandlerFunc; any http.Handler will do. Sticking to the http.Handler interface instead of the concrete HandlerFunc type will make everything easily composable:

func basicAuth(handler http.Handler) http.Handler { // Note: http.Handler, not http.HandlerFunc
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                user, pass, ok := r.BasicAuth()
                if !ok {
                        // TODO
                }
                if user != "UserName" || pass != "Password" {
                        w.WriteHeader(401)
                        w.Write([]byte("Unauthorised.\n"))
                        return
                }

                handler.ServeHTTP(w, r)
        })
}

func TestFoo(t *testing.T) {
        req, err := http.NewRequest("GET", "/abc/testfile.txt", nil)
        if err != nil {
                t.Fatal(err)
        }
        req.SetBasicAuth("UserName", "Password")
        rr := httptest.NewRecorder()

        // composition is trivial now
        sf := &slashFix{
                mux: http.HandlerFunc(handle),
        }
        handler := basicAuth(sf)
        handler.ServeHTTP(rr, req)

        // assert correct rr
}
Sign up to request clarification or add additional context in comments.

2 Comments

I'm not sure that I understood your intended test case, but since everything is an http.Handler now you can plug the handlers together however you like.
This is helpful, thanks! When there were both http.Handler and http.HandleFunc, it was confusing for me. Now, it is clear.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.