有只小跳蛙
受到 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 mainimport ( "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") } } } }) }}