为了编写单元测试,我们需要引入Python自带的 资深软件开发工程师业余马拉松选手。要是你听闻过“测试驱动开发”TDD: Test-, 那单元测试对你而言就并非陌生之物了。单元测试, 是一项测试工作, 其目的在于, 针对一个模块, 或者一个函数, 又或者一个类, 展开正确性检验。比如对函数abs()我们可以编写出以下几个测试用例输入正的数, 像是1、1.2、0.99这样的, 期望返回的值跟输入的一样输入负的数, 例如-1、-1.2、-0.99这种, 期望返回的值跟上输入方向相反输入0, 期望返回0输入不是数值类型的, 像None、、{}这种, 期望抛出异常。把位于上方的那些测试用例, 放置到一个用于测试的模块里头, 这将构成, 一个已然完整的单元测试。倘若单元测试得以通过, 那就表明我们所测试的这个函数能够正常运转。要是单元测试未能通过, 要么函数存在bug, 要么测试条件输入有误, 总而言之, 必须进行修复以使单元测试能够通过。单元测试通过之后会有怎样的意义呢? 要是我们针对abs()函数代码实施了修改, 仅仅需要再次运行一遍单元测试要是通过了, 那就表明我们所做的修改不会对于abs()函数原有的行为产生影响, 要是测试并未通过, 那就意味着我们做的修改和原有行为不相符, 或者修改代码, 或者修改测试。采用以测试为驱动的这种开发模式, 其最大的好处在于, 能确保一个程序模块的行为, 与我们所设计的测试用例相契合。在未来进行修改之际, 能够在极大程度上确保该模块的行为依旧是正确无误的。我们着手去编写一个Dict类, 这个类所展现出的行为跟dict完全相同, 然而却能够借助属性予以访问, 其使用方式如同下面这样: d Dict(a1, b2) d[a] 1 d.a 1.py代码如下class Dict(dict): def __init__(self, **kw): super().__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(rDict object has no attribute %s % key) def __setattr__(self, key, value): self[key] value为了编写单元测试我们需要引入自带的模块编写.py如下import unittest from mydict import Dict class TestDict(unittest.TestCase): def test_init(self): d Dict(a1, btest) self.assertEqual(d.a, 1) self.assertEqual(d.b, test) self.assertTrue(isinstance(d, dict)) def test_key(self): d Dict() d[key] value self.assertEqual(d.key, value) def test_attr(self): d Dict() d.key value self.assertTrue(key in d) self.assertEqual(d[key], value) def test_keyerror(self): d Dict() with self.assertRaises(KeyError): value d[empty] def test_attrerror(self): d Dict() with self.assertRaises(AttributeError): value d.empty编写单元测试时我们需要编写一个测试类从.继承。以test起始的方法, 那便是测试方法, 并非以test起步开头的方法, 并不会认作为是测试方法, 于测试之际是不会被予以执行的。搞每一种类别的测试, 都得去编写一个()方法, 鉴于.给出了好多内置的条件判断, 我们仅仅只需调用这些方法, 就能断定输出是不是我们所期盼的, 最常被使用的断言乃是():self.assertEqual(abs(-1), 1) # 断言函数返回的结果与1相等还有一种关键的断言情形是, 期望抛出特定类型的错误, 举例来说, 当借助d去访问不存在的键时, 这种断言就会引发抛出:with self.assertRaises(KeyError): value d[empty]而通过d.empty访问不存在的key时我们期待抛出with self.assertRaises(AttributeError): value d.empty运行单元测试一但将单元测试编写完成, 那我们便能够去运行单元测试了。最为简单的运行途径是于.py的 处添加上两行代码:if __name__ __main__: unittest.main()这样就可以把.py当做正常的脚本运行$ python mydict_test.py另一种方法是在命令行通过参数-m 直接运行单元测试$ python -m unittest mydict_test ..... ---------------------------------------------------------------------- Ran 5 tests in 0.000s OK因如此这般能够一回批量运转诸多单元测试, 所以这是被推荐的做法, 而且, 存在不少工具能够自行去运行这些单元测试。处于开发阶段期间, 不少情形下我们期望再三去执行某一项测试方法, 举例来说, 并非每一回都运行全部的测试方法的那种情况, 能够借助指定.class.来运行单个测试方法:$ python -m unittest mydict_test.TestDict.test_attr . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK其中, 它是文件名, 这个文件名不包含.py, class属于测试类, 它是被指定的测试方法名。假如期望去付诸实施那以及那这两个测试方式, 我们自能够传递-k这个参数, 借由attr予以匹配:$ python -m unittest mydict_test -k attr -v test_attr (mydict_test.TestDict.test_attr) ... ok test_attrerror (mydict_test.TestDict.test_attrerror) ... ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK查看上述所提及的命令, 其中, -v参数可以将具体实施的测试方法打印出来, -k attr参数把包含attr的测试方法筛选出来了。由此能够看出, 单元测试的执行是相当灵活的。setUp与能够于单元测试里撰写两个特别的setUp()以及()方法, 这两个方法会在每一回调用一个测试方法之前与之后各自被执行。设定起来的方法究竟有啥用途? 假定你所进行的测试需要开启一个数据库, 在这种情况下, 能够于设定起来的方法当中去衔接数据库, 在后一空方法里面去关闭数据库, 如此一来, 就无需在每一个测试方法里反复书写相同的代码了:class TestDict(unittest.TestCase): def setUp(self): print(setUp...) def tearDown(self): print(tearDown...)练习针对于类去开展编写单元测试, 最终发觉测试呈现不通过的状况, 请对类作出修改, 以便使得测试能够通过:import unittest class Student(object): def __init__(self, name, score): self.name name self.score score def get_grade(self): if self.score 60: return B if self.score 80: return A return C class TestStudent(unittest.TestCase): def test_80_to_100(self): s1 Student(Bart, 80) s2 Student(Lisa, 100) self.assertEqual(s1.get_grade(), A) self.assertEqual(s2.get_grade(), A) def test_60_to_80(self): s1 Student(Bart, 60) s2 Student(Lisa, 79) self.assertEqual(s1.get_grade(), B) self.assertEqual(s2.get_grade(), B) def test_0_to_60(self): s1 Student(Bart, 0) s2 Student(Lisa, 59) self.assertEqual(s1.get_grade(), C) self.assertEqual(s2.get_grade(), C) def test_invalid(self): s1 Student(Bart, -1) s2 Student(Lisa, 101) with self.assertRaises(ValueError): s1.get_grade() with self.assertRaises(ValueError): s2.get_grade() if __name__ __main__: unittest.main()小结单元测试能够对某个程序模块的行为进行有效的测试, 它是未来重构代码时给予信心的保证。单元测试的测试用例要覆盖常用的输入组合、边界条件和异常。单元测试代码需极为简单, 倘若测试代码太过繁杂, 那么测试代码自身便可能存在漏洞。单元测试要是通过了, 可不就意味着程序就一定不存在bug了, 不过若是单元测试没通过, 那程序必定是存在bug的。参考源码.py.py