首页 > 解决方案 > 如何优雅地测试 Go 中包含多个客户端的方法?

问题描述

我有一个Client包含多个客户端(etcd 和 libvirt)的结构。就像是:

type Client struct {
  etcd    *clientv3
  libvirt *libvirt.Connect
}

一旦我的图书馆的客户想要关闭它的句柄,我想关闭这两个。所以我有:

func (c *Client) Close() error {
    c.etcd.Close()
    c.libvirt.Close()
    // Error handling excluded for brevity
}

什么是测试这个的优雅方法?我目前最好的选择是创建两个接口,一个用于两个包装客户端中的每一个。这些接口将包括我的库使用的两个客户端的每一个方法。这应该使得传递某种模拟而不是真正的客户变得相对容易。这可能是前进的方向,但感觉很尴尬。

我还有哪些其他选择?

标签: unit-testinggo

解决方案


正如我在评论中提到的,您可以创建一个ClosableClient如下所示的。由于您的每个客户都有Close方法,因此您可以这样做。在您的测试文件中,您可以创建只需要实现Close方法的模拟客户端。您不需要使接口实现所有方法。在您的代码中,您可以使用类型断言将其转换ClosableClient为特定的客户端以访问其功能。是类型断言的一个很好的例子。

我添加了代码片段来展示如何使用类型断言来获取底层结构。测试文件中的模拟客户端不需要实现 Foo 和 Bar 方法,因为接口ClosableClient只需要Close方法。

type ClosableClient interface {
    Close()
}

type Etcd struct{}

func (e *Etcd) Close() {
    fmt.Println("etcd closing")
}

func (e *Etcd) Foo() {
    fmt.Println("etcd foo")
}

type Libvirt struct{}

func (l *Libvirt) Close() {
    fmt.Println("libvirt closing")
}

func (l *Libvirt) Bar() {
    fmt.Println("libvirt bar")
}

type Client struct {
    etcd    ClosableClient
    libvirt ClosableClient
}

func (c *Client) Close() {
    c.etcd.Close()
    c.libvirt.Close()
}

func (c *Client) FooBar() {
    etcd, ok := c.etcd.(*Etcd)
    if !ok {
        panic("etcd is of incorrect type")
    }

    etcd.Foo()

    libvirt, ok := c.etcd.(*Libvirt)
    if !ok {
        panic("libvirt is of incorrect type")
    }

    libvirt.Bar()
}

推荐阅读