如何在Golang中使用成员函数正确模拟结构?

时间:2017-06-14 13:22:41

标签: unit-testing testing go struct

我有两个结构:FunctionalityClientTestClient,都是Interface。我有一个Client类型的全局变量Interface。我将Client分配给实际客户端或模拟客户端,具体取决于它是测试还是正常运行。

Interface有一个我想在测试中模拟的方法Request。也就是说,我想:

  • 记录传递给函数
  • 的参数
  • 从函数
  • 返回一些任意定义的返回值

所以结构看起来像这样:

type TestClient struct {
     recordedArgs []interface{}
     returnValues []interface{}
}
func (c *TestClient) Request(body io.Reader, method string, endpoint string, headers []Header) ([]byte, error) {
    c.recordedArgs = append(c.recordedArgs, []interface{}{body, method, endpoint, headers})  // this can't be typed if I want the code to be reusable
        if len(c.returnValues) != 0 {
        last := c.returnValues[0]
        c.returnValues = c.returnValues[1:]
        return last.([]byte), nil
    }
    return nil, nil
}

我这样使用它:

testClient := TestClient{
    returnValues: []interface{}{
        []byte("arbitrarily defined return value"),
        []byte("this will be returned after calling Request a second time"),
    }
}
Client = &testClient
// run the test
// now let's check the results
r1 := testClient.recordedArgs[1].([]interface{})  // because I append untyped lists to recordedArgs
assert.Equal(t, "POST", r1[1].(string))
assert.Equal(t, "/file", r1[2].(string))
// and so on

现在问题。

我有一些结构要我这样嘲笑。目前我只是为每个结构复制并粘贴上面的代码。但这真的很糟糕,我希望以某种方式抽象出模拟逻辑。我也会接受类似Mockito的when之类的东西:当使用特定参数调用模拟函数时,返回一个特定值并记录调用。

如何在Golang中使用成员函数正确模拟结构?

1 个答案:

答案 0 :(得分:0)

如果您要为HTTP API模拟客户端,您可能只想使用httptest.Server,这会极大地简化这一过程。而不是模拟客户端,模拟客户端连接到的服务器。它非常易于使用,您仍然可以记录请求方法,路径,正文等,以及以与模拟客户端相同的方式返回任意响应值。

如果这不是一个选项,你可以抽象出你的模拟方法,使其可重复使用:

type TestClient struct {
     recordedArgs [][]interface{}
     returnValues []interface{}
}

func (c *TestClient) mock(args ...interface{}) interface{} {
    c.recordedArgs = append(c.recordedArgs, args)
    if len(c.returnValues) != 0 {
        last := c.returnValues[0]
        c.returnValues = c.returnValues[1:]
        return last
    }
    return nil
}

func (c *TestClient) Request(body io.Reader, method string, endpoint string, headers []Header) ([]byte, error) {
    return c.mock(body,method,endpoint,headers).([]byte), nil
}

这会将特定于使用情况的方法减少到一行。