duanbin3021 2018-05-26 16:41
浏览 56

测试中的覆写Go方法

So I have this Client struct that has a method UserByID that makes a HTTP request to an endpoint for a User. I want to unit test this function but also not make an actual HTTP request in the function c.Request. I want to stub that function with a response and error I can control.

func (c Client) UserByID(id string) (u User, err error) {
  v := url.Values{}
  v.Set("id", id)
  opts := Request{
    HTTP: http.Request{
        Method: http.MethodGet,
        Form:   v,
    },
    URL: 'some/endpoint/users',
  }
  resp, err := c.Request(opts)
  err = json.Unmarshal(resp, &u)
  return
}

Here's what the stub looks like:

type mockClient struct {
  Client
  fakeUser  User
  fakeError error
}

func (mc mockClient) Request(opts Request) (resp []byte, err error) {
  resp, err = json.Marshal(mc.fakeUser)
  err = mc.fakeError
  return
}

In a single test I have something like:

client := mockClient{
  fakeUser: User{},
  fakeError: nil,
}
user, err := client.UserByID(c.id)

Then I can assert the return values from client.UserByID. In this example I'm trying to override the client.Request function but I understand Go is not an inheritance-type of language. In my tests, my mockClient.Request function is not being called. The original client.Request is still being called.

I then assume that my approach is not right then. How can I test client.UserByID without actually calling the real client.Request function within it? Should the design of my methods be different?

  • 写回答

2条回答 默认 最新

  • dousi1961 2018-05-26 19:18
    关注

    To accomplish what you need, you can re-structure your code just a little bit.

    You can find a full working example here: https://play.golang.org/p/VoO4M4U0YcA

    And below is the explanation.

    First, declare a variable function in your package to encapsulate the actual making of the HTTP request:

    var MakeRequest = func(opts Request) (resp []byte, err error) {
        // make the request, return response and error, etc
    }
    

    Then, in your Client use that function to make the request:

    func (c Client) Request(opts Request) (resp []byte, err error) {
        return MakeRequest(opts)
    }
    

    In that way, when you actually use the client, it will make the HTTP request as expected.

    But then when you need to test, you can assign a mock function to that MakeRequest function so that you can control its behaviour:

    // define a mock requester for your test
    
    type mockRequester struct {
        fakeUser  User
        fakeError error
    }
    
    func (mc mockRequester) Request(opts Request) (resp []byte, err error) {
        resp, err = json.Marshal(mc.fakeUser)
        err = mc.fakeError
        return
    }
    
    // to use it, you can just point `MakeRequest` to the mock object function
    
    mockRequester := mockRequester{
        fakeUser:  User{ ID: "fake" },
        fakeError: nil,
    }
    MakeRequest = mockRequester.Request
    
    评论

报告相同问题?

悬赏问题

  • ¥15 如何实现H5在QQ平台上的二次分享卡片效果?
  • ¥15 python爬取bilibili校园招聘网站
  • ¥30 求解达问题(有红包)
  • ¥15 请解包一个pak文件
  • ¥15 不同系统编译兼容问题
  • ¥100 三相直流充电模块对数字电源芯片在物理上它必须具备哪些功能和性能?
  • ¥30 数字电源对DSP芯片的具体要求
  • ¥20 antv g6 折线边如何变为钝角
  • ¥30 如何在Matlab或Python中 设置饼图的高度
  • ¥15 nginx中的CORS策略应该如何配置