为什么要Mock
在一个复杂项目中,项目可能依赖于多个组件(DB,Cache,File等)。这些依赖都属于项目中的强依赖,如果没有这些组件,项目是不完整的,或者说某个功能是运行不了的。
单元测试有两个目标,一个是幂等,一个是稳定。
幂等:重复运行一个测试的case,每次的结果都是一样的
稳定:单元测试相互隔离,在任何时间,任何函数,任何环境都能独立的运行
那要实现这个目标,因为我们有这么多依赖,如果直接写单元测试,调用db,这样单元测试肯定是不稳定的,因为会依赖于网络的传输。那要怎么写能实现稳定的单元测试呢?
对于强依赖的项目,如何实现稳定这个目标呢?因为我们有这么多强依赖,如果直接写单元测试,调用DB,这样的单元测试是不稳定的,因为会依赖于网络的传输,如果DB刚好挂了呢?那么怎样才能实现一个不依赖组件的稳定单元测试呢?
要解决这个问题,我们需要用到Mock,单元测试的过程中需要用到mork机制
什么是Mock
github地址:bouk-monkey
快速Mock函数
- 为一个函数打桩
- 为一个方法打桩
下面我们来看一下Mock,打桩可以理解为用一个函数A替换成函数B,函数B就是原函数,函数A就是一个打桩函数。
以函数打桩为例,有两个方法,一个是Patch,入参是原函数,replacement是我们需要打桩的函数,另一个是unpatch,是为了在保证在测试结束以后把这个桩卸载掉。
monkey包的实现主要是在运行时,通过go的unsafe包,将内存中函数的地址替换成运行时函数地址,这样其实最终在测试的时候调用的是打桩函数,就实现了Mork的功能
做个测试
不用Mock
可以看到,这里的单元测试是依赖于本地log文件的,一旦log文件被篡改,被删除,那么这个单元测试就失败了
func ReadFirstLine() string { buf, err := ioutil.ReadFile("log") if err != nil { panic(err) } return string(buf) }
func TestProcessFirstLine(t *testing.T) { firstLine := ReadFirstLine() assert.Equal(t, "wxf", firstLine) }
使用Mock
这里对ReadFirstLine
进行打桩,让其始终返回"wxf",通过一个Mork,就实现了单元测试不对本地文件的强依赖。这样我们的单元测试就可以在任何时间,任何环境去执行,完全不依赖于本地文件,测试依然能够正常运行
func TestProcessFirstLineWithMock(t *testing.T) { monkey.Patch(ReadFirstLine, func() string { return "wxf" }) defer monkey.Unpatch(ReadFirstLine) line := ReadFirstLine() assert.Equal(t, "wxf", line) }