https://segmentfault.com/a/
有如下函数,简单来说就是有错误则直接返回,没错误则执行f
函数。
1 2 3 4 5 6 7
| func (t *transaction) Do(f func()) *transaction { if t.fail || t.rollback || t.finish { return t } f() return t }
|
函数很简单,但如何测试呢,简单但丑陋的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| func Test_func(t *testing.T) { isCalled := false f := func() { isCalled = true } trans := New() trans.Do(f) if isCalled { } }
|
在f
中修改外部变量,然后判断变量是否变化就可以知道f
是否被执行。但我们一般需要测试多种情况,比如对于Do
函数,我们需要将 t.fail
t.rollback
t.finish
设置不同值进行测试,将上面测试代码扩充(如果需要测试这三个变量组合的情况,代码就更长了):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| func Test_func(t *testing.T) { isCalled1 := false isCalled2 := false isCalled3 := false f1 := func() { isCalled1 = true } f2 := func() { isCalled2 = true } f3 := func() { isCalled3 = true } trans := New() trans.Do(f1) trans.Do(f2) trans.Do(f3) if isCalled1 { } if isCalled2 { } if isCalled3 { } }
|
在上面代码中f1
f2
f3
函数的逻辑都一样,这时可以通过使用闭包来消除冗余代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| func Test_func(t *testing.T) { genTestFunc := func() (func(), func() bool) { isCalled := false return func() { isCalled = true }, func() bool { return isCalled } } f1, f1Called := genTestFunc() f2, f2Called := genTestFunc() f3, f3Called := genTestFunc() trans := New() trans.Do(f1) trans.Do(f2) trans.Do(f3) if f1Called() { } if f2Called() { } if f3Called() { } }
|
解释一下,genTestFunc
返回值是两个函数,第一个函数可传入Do
中,第二个函数用来判断是否被Do
调用。
粗略看改动前后代码行数基本相同,但如果f
变复杂或者需要更多的测试case时,改动后的代码更加简洁,易于维护。