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的文档。
    祝大家新年快乐,万事如意^_^

 

posted @ 2013-02-06 17:20  竹风抚荷塘  阅读(1963)  评论(0编辑  收藏  举报