Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing http handlers in golang mocking dependencies

At the moment I try to establish best practices for unit testing go handlers. I need to mock dependencies, but therefore I have to be able to access / mock these dependencies.

There I some solutions I do not want to consider like for example global variables / application state. Or having all handlers as functions of a struct holding the dependencies member variables.

I was kind of satisfied with a solution where I injected the needed dependencies of a handler in the following way:

func helloHandler(db *DbService) http.HandlerFunc {
  return func(w http.ResponseWriter, r *httpRequest) {
    // handler code goes here
  }
}

Then I am able to provide this handler for routing:

http.HandleFunc("/hello", helloHander(myDbService))

I can also test it easily:

helloHandler(myDbService)(req, respRecorder)

But when I use for example gorilla mux (what I haven't considered from the beginning) I get another problem.

Gorilla/mux adds extra semantics like filtering by Method (GET / POST), providing path parameters etc.

So I should need to test the resulting gorilla router. But then I am again not able to inject my mock dependencies anymore.

When I get the router back everything (dependencies) is already set up.

But I also don't want to rebuild my Router in my tests because of DRY!

So for now I don't really have a good solution for the problem of conveniently setting up my routes but also being able to mock the dependencies.

Any idea?

like image 978
Subby Avatar asked Sep 06 '25 16:09

Subby


1 Answers

I personally changed my Router function into a method with a Receiver, so I can create Mock receivers:

Production code:

func main(){
    router := mux.NewRouter()
    version.AddRouter(router, contextRoot)
    someStruct := SomeStruct{SomeDependency{}}
    router.HandleFunc(contextRoot, someStruct.HandleRequests).
           Methods(http.MethodGet)
}

func (s SomeStruct) HandleRequests(writer http.ResponseWriter, reader *http.Request) {
    ...
}

func (s SomeDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
    ...
    return SomeReturnStruct{}
}

Unit test:

type mockWriter struct {}
type someMockDependency struct {}
func Test_HandleRequest1(t *testing.T) {

    someStructMockDeps := SomeStruct{someMockDependency{}}
    someStructMockDeps.HandleRequests(mockWriter{}, &http.Request{URL: &url.URL{RawQuery:"http://dummy-query.com"}});

    assert.Equal(...)
}
func (someMockDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
    ...
    return SomeReturnStruct{}
}

Integration test:

func TestHandlerFuncIntg(t *testing.T) {
    if testing.Short() {
        println("skipping")
        t.Skip()
     }

    req, err := http.NewRequest("GET", "/hello?param=value", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.NewRecorder()
    someStructMockDeps := SomeStruct{someMockDependency{}}
    handler :=   http.HandlerFunc(someStructMockDeps.HandleRequests)

    handler.ServeHTTP(rr, req)

    assert.Equal(t, http.StatusOK, rr.Code)
    assert.Equal(t, "This is my result", rr.Body.String())
}

func (someMockDependency) SomeFunctionCalledFromHandler(...) SomeReturnStruct {
    ...
    return SomeReturnStruct{}
}

SomeStruct declares the dependencies (as Dependency Injection) so they can be overridden for tests.

like image 142
alexbt Avatar answered Sep 09 '25 13:09

alexbt