Python札记 -- 测试优先
竹风看的第一本有关Python的书是《Dive Into Python》(简称DIP),本人觉得这本书写得是相当不错的(当然竹风无意卷入关于这本书是好是坏的争论,只要找到适合自己的资料和学习方式就好)。读《DIP》的时候,竹风对HTML和XML是一窍不通(当然现在也只是会点基本的),所以这两章是看得云里雾里的。当然也有给竹风带来震撼的章节,比如“单元测试”和“测试优先编程”。
《DIP》中给出了一个罗马数字的程序例子,里面用到了Python中的unittest模块,这段时间竹风看了下Python 2.7的文档,结合自己的工作体会,做一个简单总结,跟大家分享一下。当然,如果错误,是竹风学艺不精,欢迎大家的指正和讨论^_^。
参考资料:《Dive Into Python》 Mark Pilgrim 第13章.单元测试 第14章.测试优先编程 (强力推荐读一下,里面测试驱动开发的思想很值得了解)
《ActivePython Documentation》 这个是在windows上安装 ActiveState ActivePython 2.7 附带的
一、简单的例子
竹风琢磨着,程序员最好的交流就是用代码了,“翠花,上代码~~”
1 #!/usr/bin/env python 2 #coding: utf-8 3 4 import random 5 import unittest #首先将 unittest 模块导入 6 7 class TestSequenceFunctions(unittest.TestCase): #继承了TestCase 8 9 def setUp(self): #初始化函数,生成10个元素的列表 10 self.seq = range(10) 11 12 def test_shuffle(self): 13 #测试shuffled操作是否会丢失元素 14 random.shuffle(self.seq) 15 self.seq.sort() 16 self.assertEqual(self.seq, range(10)) #用到了一个判断相等的断言 17 18 #如果传入一个不可变的对象,比如元组,应抛出一个异常 19 self.assertRaises(TypeError, random.shuffle, (1,2,3)) 20 21 def test_choice(self): 22 #测试choice操作返回的值是否属于seq 23 element = random.choice(self.seq) 24 self.assertTrue(element in self.seq) #用到了一个判断值为真的断言 25 26 def test_sample(self): 27 #测试sample操作返回的结果是否属于seq 28 self.assertRaises( #当传入的参数有误时,应抛出一个异常 29 ValueError, random.sample, self.seq, 20 30 ) 31 for element in random.sample(self.seq, 5): 32 self.assertTrue(element in self.seq) #用到了一个判断值为真的断言 33 34 if __name__ == '__main__': 35 unittest.main()
竹风在文档的例子上稍微加了点注释,这个例子对各位园友来说应该是小菜一碟了。如果使用的是Python 2.7的话,还可以将28-30行修改为:
1 with self.assertRaises(ValueError): 2 random.sample(self.seq, 20)
运行起来应该是这个样子的:
1 $ python TestSequenceFuncions.py 2 ... 3 ---------------------------------------------------------------------- 4 Ran 3 tests in 0.000s 5 6 OK
如果想看稍微详细点的测试信息,可以将最后一行 “unittest.main()” 修改如下:
suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
unittest.TextTestRunner(verbosity=2).run(suite)
这时候输出信息就会稍微详细一点:
$ python TestSequenceFuncions.py test_choice (__main__.TestSequenceFunctions) ... ok test_sample (__main__.TestSequenceFunctions) ... ok test_shuffle (__main__.TestSequenceFunctions) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.000s OK
如果还觉得不够详细,那么可以在命令行上加 -v 选项:
$ python -v TestSequenceFuncions.py
不过这个输出到屏幕的信息已经达到刷屏的程度了。。。大家还是慎用。。。
二、测试的顺序与命名规范
不知道各位园友注意到了没有,测试的顺序是跟class里面定义的def的顺序无关的。
在class的定义顺序是:(test_shuffle,test_choice,test_sample);
而实际测试的时候是:(test_choice,test_sample,test_shuffle);
根据竹风的个人经验,测试的顺序应该是在去掉 “test_” 前缀后,按照字母顺序排序。
还需要提到的一点是,需要测试的 def 需要以 “test_” 为前缀。针对这点,我们可以写一个 “make_test_data” 的函数准备测试数据,再写一个 “clean_test_data” 的函数清理测试数据。
PS:根据《DIP》里面提到关于测试的两个基本原则:
一、每个测试用例只回答一个问题。
二、每个测试用例必须可以与其他测试用例隔离工作,每个测试用例是一个“孤岛”。
根据这两个基本原则,的确应该与定义他们的顺序无关的。
三、初始化(setUp)和清理工作(tearDown)
如果竹风用“make_test_data”来创建测试数据,竹风的基友用的是"create_test_data",一千个程序员可能就带来一千种命名方式,这五花八门的函数名字可就真的让人头疼了。
根据Python里“import this”的精神,应该会有专门的函数来执行初始化或者清理工作的。也的确有这两个函数,setUp 与 tearDown 分别负责 初始化 和 清理 工作。
setUp函数总是第一个调用,而tearDown函数则总是在最后一个调用。现在函数调用的顺序也大概理清楚了:setUp --> test_* --> tearDown。
四、常用的断言方法(assert methods)
最后水一下几个常用的断言。
assertEqual(first, second, msg=None)
assertNotEqual(first, second, msg=None)
判断first和second是否相等(不等),msg为测试失败时给出的信息。msg一般使用默认值就行
assertTrue(expr, msg=None)
assertFalse(expr, msg=None)
判断expr的值是否为真(假),msg为测试失败时给出的信息。
assertRaises(exception, callable, *args, **kwds) #参数魔法里面有提到过的哦
assertRaises(exception)
判断是否抛出一个异常,*args和 **kwds 为调用 callable 需要传的参数,注意这里callable的传参数方式。可参照上面的例子。
到这里竹风这个简单的分享也结束了,用上面的这些内容,写个简单的测试用例问题应该不大了。
测试个人感觉上是一个很大的领域,竹风也只是刚接触。
建议有兴趣的园友看一下《DIP》的13、14章,当然也可以看下Python的文档。
祝大家新年快乐,万事如意^_^