dongzhang0418 2017-05-29 19:35
浏览 47

在Golang模拟依赖项中对HTTP处理程序进行单元测试

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?

  • 写回答

1条回答 默认 最新

  • duanrebo3559 2017-05-30 00:21
    关注

    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.

    评论

报告相同问题?

悬赏问题

  • ¥15 微带串馈天线阵列每个阵元宽度计算
  • ¥15 关于无人驾驶的航向角
  • ¥15 keil的map文件中Image component sizes各项意思
  • ¥30 BC260Y用MQTT向阿里云发布主题消息一直错误
  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 Centos / PETGEM
  • ¥15 划分vlan后不通了