Python TDD(Testing Driven Development)详解
coding的形成过程应该是从special case入手,找到general solving methods,为了提高编码效率,TDD是一个不错的办法。
实例:
输入——一个自然数list
功能——按照list的顺序先输出list中所有偶数,再输出剩下的奇数
输出——功能化后的list
1.实例实现
应用之前提到的python list生成的优化写法:
def f(L):
return [e for e in L if e % 2 == 0] + [e for e in L if e % 2 == 1]
这种写法效率不高,因为要2次遍历数组L分别找偶数和奇数,可以优化为:
def f(L):
even_member = []
odd_member = []
for e in L:
if e % 2 == 0:
even_member.append(e)
else:
odd_member.append(e)
return even_member + odd_member
2.Test设计
这个实例是关于奇偶数的排序,所以应该包含如下的测试
(1)[]
(2)[奇数]
(3)[偶数]
(4)[奇数, 奇数]
(5)[偶数,偶数]
(6)[奇数,偶数]
(7)[偶数,奇数]
(8)[偶数,偶数,偶数,偶数]
(9)[奇数, 奇数,奇数, 奇数]
(10)[奇数,偶数,偶数...]
3.原程序中人肉输出检验
使用print()函数显示出来,人肉核对是否正确。
print(f([]))
print(f([0]))
print(f([1]))
print(f([0, 2]))
print(f([1, 3]))
print(f([0, 3]))
print(f([3, 0]))
print(f([2, 0, 8, 6]))
print(f([5, 1, 3, 9]))
print(f([2, 7, 8, 3, 4]))
4.原程序中设置检验函数
人肉检验的问题不仅是伤身,更是会影响函数调用。如果其他程序调用了一个包含这么多人肉输出代码的程序,跑出来的结果会非常尴尬。设想如果你打开微信,结果微信由于调用了这样一个程序,导致你的界面上有若干测试结果,只能用灾难形容。
一个小进步是先解决程序调用问题。
def f(L):
return [e for e in L if e % 2 == 0] + [e for e in L if e % 2 == 1]
print("Name of module: ", __name__)
if __name__ == '__main__':
print('\nTesting [], result should be [].')
print(f([]) == [])
print('\nTesting [0], result should be [0].')
print(f([0]) == [0])
print('\nTesting [1], result should be [1].')
print(f([1]) == [1])
print('\nTesting [0, 1], result should be [0, 1].')
print(f([0, 1]) == [0, 1])
print('\nTesting [1, 0], result should be [0, 1].')
print(f([1, 0]) == [0, 1])
print('\nTesting [3, 0, 11, 17, 10, 11, 17, 15, 18, 5], result should be [0, 10, 18, 3, 11, 17, 11, 17, 15, 5].')
print(f([3, 0, 11, 17, 10, 11, 17, 15, 18, 5]) == [0, 10, 18, 3, 11, 17, 11, 17, 15, 5])
由于在人肉输出的时候有了限制条件if __name__ == '__main__',含义是这个程序名称是不是main,所以解决了刚才人肉输出的调用问题。
在函数调用时,当前的程序名称是main,被调用的程序名称是其“文件名”。这样在调用上述函数的时候,if条件不满足,各种检验不会运行。
另外,对人肉工作做了一点优化,不再需要人肉核对所有list元素,而是直接给出True or False的结果。
5.写测试程序
实际工作中,不同module是严格分开的,测试程序一般要在新的python文件下编码,需要测试的时候可以方便调用。
def f(L):
return [e for e in L if e % 2 == 0] + [e for e in L if e % 2 == 1]
if __name__ == '__main__':
import test
test.test()
上述程序在源程序相同路径下存放有一个文件名为"test"的测试程序,import后运行其中的test()函数。这里注意,不要忘记写if条件,否则调用的时候可能会由于路径问题报错。
6.Python prompt测试
分开写测试文件确实常见,但是对于小的函数未免太过麻烦,所以最推荐的做法是利用python prompt做测试。
def f(L):
'''
>>> f([])
[]
>>> f([0, 1])
[0, 1]
>>> f([1, 0])
[1, 0]
'''
return [e for e in L if e % 2 == 0] + [e for e in L if e % 2 == 1]
import doctest
doctest.testmod()
写法是在def函数下一行缩减后,写入多行注释。在注释中,>>>代表python的prompt,后面写的是设计好的测试,换行后给出预期的结果。如果结果不同,python运行时会报错。