GOLANG最容易做测试MOCK
原文:https://gocn.io/article/353 测试时,一些底层的库非常难以MOCK,比如HASH摘要算法,怎么MOCK?假设有个函数,是用MD5做摘要: func digest(data []byte,h hash.Hash) ([]byte,error) {
if _,err = h.Write(data); err != nil {
return nil,errors.Wrap(err,"hash write")
}
d := h.Sum(nil)
if len(d) != 16 {
return nil,errors.Errorf("digest's %v bytes",len(d))
}
return d,nil
}
难以覆盖的因素有几个:
用GOLANG就可以完美解决上面所有的覆盖问题,先上代码: type mockMD5Write struct {
hash.Hash
}
func newMockMD5Write(h hash.Hash) hash.Hash {
return &mockMD5Write{ h,}
}
func (v *mockMD5Write) Write(p []byte) (n int,err error) {
return 0,fmt.Errorf("mock md5")
}
就这么简单?对的,但是不要小看这几行代码,深藏功与名~ 组合接口结构体 class Hash {
public: virtual int Write(const char* data,int size) = 0;
public: virtual int Sum(const char* data,int size,char digest[16]) = 0;
public: virtual int Size() = 0;
};
class MD5 : public Hash {
// 省略了实现的代码
}
class mockMD5Write : public Hash {
private: Hash* imp;
public: mockMD5Write(Hash* v) {
imp = v;
}
public: int Write(const char* data,int size) {
return 100; // 总是返回个错误。
}
};
是么?错了, class mockMD5Write : public Hash {
private: Hash* imp;
public: mockMD5Write(Hash* v) {
imp = v;
}
public: int Write(const char* data,int size) {
return 100; // 总是返回个错误。
}
public: int Sum(const char* data,char digest[16]) {
return imp->Sum(data,size,digest);
}
public: int Size() {
return imp->Size();
}
};
对比下够浪的接口组合,因为组合了一个 type mockMD5Write struct {
hash.Hash
}
func (v *mockMD5Write) Write(p []byte) (n int,fmt.Errorf("mock md5")
}
这个可不是少写了几行代码的区别,这是本质的区别,我鸡冻的辩解道~如果这个接口有十个函数,我们要测试100个接口呢?这个MOCK该怎么写?另外,这个实际上是OO和GOLANG的细微差异,GOLANG的接口是契约,只要满足就可以,面向的全是动作,GOLANG像很多函数组合,它没有类体系的概念,也就是它的结构体不用明显符合哪个接口和哪个接口它才是合法的,实际上它可以符合任何适配的接口,也就是 复杂错误我们用了errors这个包,用来返回复杂错误,可以看到堆栈信息,对于utest也是一样,能看到堆栈对于解决问题也很重要。可以参考Error最佳实践。比如打印信息: --- FAIL: TestDigest (0.00s)
digest_test.go:45: digest,mock md5
hash write data
_/Users/winlin/git/test/utility.digest
/Users/winlin/git/test/utility.go:46
_/Users/winlin/git/test/TestDigest
/Users/winlin/git/test/digest_test.go:42
testing.tRunner
/usr/local/Cellar/go/1.8.1/libexec/src/testing/testing.go:657
runtime.goexit
/usr/local/Cellar/go/1.8.1/libexec/src/runtime/asm_amd64.s:2197
测试代码: func TestDigest(t *testing.T) {
if _,err := digest(nil,newMockMD5Write(md5.New())); err == nil {
t.Error("should failed")
} else {
t.Errorf("digest,%+v",err)
}
}
当然这个地方是主动把error打印出来,因为用例就是应该要返回错误的,一般情况是: func TestXXX(t *testing.T) {
if err := pfn(); err != nil {
t.Errorf("failed,err)
}
}
这样就可以知道堆栈了。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |