2 回答
![?](http://img1.sycdn.imooc.com/5333a1bc00014e8302000200-100-100.jpg)
TA贡献1876条经验 获得超7个赞
正如我在评论中提到的,您可以创建一个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()
}
![?](http://img1.sycdn.imooc.com/5458632800010f8802200220-100-100.jpg)
TA贡献1824条经验 获得超8个赞
受到 poWar 说我的想法很好的评论的鼓舞,我继续前进:
我更改了我的Client结构以将接口用于我的 libvirt 和 etcd 连接:
type EtcdClient interface {
}
type LibvirtClient interface {
}
type Client struct {
etcd EtcdClient
libvirt LibvirtClient
}
当我尝试编译包时,我收到一条类似这样的错误消息:
./main.go:17:18: c.etcd.Close undefined (type EtcdClient is interface with no methods)
./main.go:21:24: c.libvirt.Close undefined (type LibvirtClient is interface with no methods)
不奇怪。然后我在接口中添加了最简单的 Close() 方法:
type EtcdClient interface {
Close()
}
type LibvirtClient interface {
Close()
}
再次编译给了我:
./main.go:56:10: cannot use etcd (type *clientv3.Client) as type EtcdClient in assignment:
*clientv3.Client does not implement EtcdClient (wrong type for Close method)
have Close() error
want Close()
./main.go:62:13: cannot use lv (type *libvirt.Connect) as type LibvirtClient in assignment:
*libvirt.Connect does not implement LibvirtClient (wrong type for Close method)
have Close() (int, error)
want Close()
然后我用它来填写接口定义:
type EtcdClient interface {
Close() error
}
type LibvirtClient interface {
Close() (int, error)
}
当然,Close这很简单,我不必经历这个,但正如我之前提到的,我在这些接口上调用了很多方法,这种方式让编译器帮助我填写接口变得非常简单定义。
对于测试,我可以制作假货(模拟?存根?我总是忘记区别)。这是完整的测试文件:
package main
import (
"errors"
"testing"
)
type FakeEtcdClient struct {
wasClosed bool
failToClose bool
}
func (f *FakeEtcdClient) Close() error {
if f.failToClose {
return errors.New("Fake Etcd failed to Close")
}
f.wasClosed = true
return nil
}
type FakeLibvirtClient struct {
wasClosed bool
failToClose bool
}
func (f *FakeLibvirtClient) Close() (int, error) {
if f.failToClose {
return 0, errors.New("Fake libvirt failed to Close")
}
f.wasClosed = true
return 0, nil
}
func TestClient_Close(t *testing.T) {
type fields struct {
etcd EtcdClient
libvirt LibvirtClient
}
tests := []struct {
name string
fields fields
wantErr bool
}{
{"Happy path", fields{&FakeEtcdClient{}, &FakeLibvirtClient{}}, false},
{"Etcd fails", fields{&FakeEtcdClient{failToClose: true}, &FakeLibvirtClient{}}, true},
{"Libvirt fails", fields{&FakeEtcdClient{}, &FakeLibvirtClient{failToClose: true}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Client{
etcd: tt.fields.etcd,
libvirt: tt.fields.libvirt,
}
if err := c.Close(); (err != nil) != tt.wantErr {
t.Errorf("Client.Close() error = %v, wantErr %v", err, tt.wantErr)
} else {
if !tt.wantErr {
// We only check if the clients have been closed if
// Client.Close() returns successfully.
if !c.etcd.(*FakeEtcdClient).wasClosed {
t.Error("Etcd connection was not closed")
}
if !c.libvirt.(*FakeLibvirtClient).wasClosed {
t.Error("Libvirt connection was not closed")
}
}
}
})
}
}
- 2 回答
- 0 关注
- 103 浏览
添加回答
举报