dongwaner1367 2017-06-23 17:33
浏览 39

如何对将http调用包装到外部服务的处理程序进行单元测试

i have a handler function which is called CreateObject. This function wraps at the same time a POST request to an external API which i dont control. If I want to unit test it, the problem I face is that I can't be posting new objects to the external service every time I run the test. So I would like to know if there is a way to mock this with Go or any workaround.

Many thanks.

package main

func main() {

router := mux.NewRouter()

router.HandleFunc("/groups", services.CreateObject).Methods("POST")
c := cors.New(cors.Options{
    AllowedOrigins:   []string{"*"},
    AllowCredentials: true,
    AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"},
    AllowedHeaders:   []string{"*"},
    ExposedHeaders:   []string{"*"},
})

handler := c.Handler(router)

http.ListenAndServe(":3000", handler)

package objects

  func CreateObject(w http.ResponseWriter, r *http.Request) {



        var newobject Object
        _ = json.NewDecoder(r.Body).Decode(&newobject )

        //Do things

        jsonStr, err := json.Marshal(newobject)
        if err != nil {
            fmt.Println(err)
            return
        }

        req, err := http.NewRequest("POST", ExternalURL+"/object", bytes.NewBuffer(jsonStr))


        if err != nil {
            fmt.Println(err)
            return
        }

        client := &http.Client{}
        resp, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        defer resp.Body.Close()

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


        if resp.StatusCode < 200 || resp.StatusCode > 299 {
            w.WriteHeader(resp.StatusCode)
            w.Header().Set("Content-Type", "application/json")
            w.Write(body)

        } else {
            w.WriteHeader(201)
        }



}
  • 写回答

1条回答 默认 最新

  • dsgwdigu84950 2017-06-23 17:46
    关注

    You have a couple options:

    1) You can define an interface that has the exported method set of the http.Client. You can then make a package-level variable of this type, which defaults to a *http.Client. Instead of using a *http.Client in CreateObject, you'd use this variable. Since it's an interface, you can mock out the client easily. The interface would look like this:

    type HTTPClient interface {
        Do(req *http.Request) (*http.Response, error)
        Get(url string) (resp *http.Response, err error)
        Post(url string, contentType string, body io.Reader) (resp *http.Response, err error)
        PostForm(url string, data url.Values) (resp *http.Response, err error)
        Head(url string) (resp *http.Response, err error)
    }
    

    Since you only call Do(), however, your mock only needs to define an actual test implementation for Do. We often use a function-field style for this:

    type MockClient struct {
        DoFunc func(req *http.Request) (*http.Response, error)
        // other function fields, if you need them
    }
    
    func (m MockClient) Do(req *http.Request) (r *http.Response, err error) {
        if m.DoFunc != nil {
            r, err = m.DoFunc(req)
        }
        return
    }
    
    // Define the other 4 methods of the HTTPclient as trivial returns
    
    var mockClient HTTPClient = MockClient{
        DoFunc: func(req *http.Request) (*http.Response, error) {
            return nil, nil
        },
    }
    
    var mockClientFail HTTPClient = MockClient{
        DoFunc: func(req *http.Request) (*http.Response, error) {
            return nil, fmt.Errorf("failed")
        },
    }
    

    2) Stand up your own HTTP mock server on a localhost port and, within your tests, change your ExternalURL variable to point to it instead. This allows you to actually test the dialing (which makes it more of a functional test than a unit test), while still effectively "mocking" the external endpoint.

    In either case, make sure you also write some regression tests to make sure the external endpoint still works as expected.

    Edit: Per dm03514, Go already has an mock HTTP server built in: https://golang.org/pkg/net/http/httptest/

    评论

报告相同问题?

悬赏问题

  • ¥15 划分vlan后不通了
  • ¥15 GDI处理通道视频时总是带有白色锯齿
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)
  • ¥15 自适应 AR 模型 参数估计Matlab程序
  • ¥100 角动量包络面如何用MATLAB绘制
  • ¥15 merge函数占用内存过大
  • ¥15 使用EMD去噪处理RML2016数据集时候的原理
  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大
  • ¥15 单片机无法进入HAL_TIM_PWM_PulseFinishedCallback回调函数