学习如何使用Python模块unittest
中的工具来测试代码
对函数进行测试
Python标准库中的模块
unittest
提供了代码测试工具。 单元测试 用于核实函数的某个方面没有问题; 测试用例 是一组单元测试,这些单元测试一起核实函数在各种情形下的行为都符合要求。良好的测试用例考虑到了函数可能收到的各种输入,包含针对所有这些情形的测试。 全覆盖式测试 用例包含一整套单元测试,涵盖了各种可能的函数使用方式。对于大型项目,要实现全覆盖可能很难。通常,最初只要针对代码的重要行为编写测试即可,等项目被广泛使用时再考虑全覆盖。要为函数编写测试用例,可先导入模块
unittest
以及要测试的函数,再创建一个继承unittest.TestCase
的类,并编写一系列方法对函数行为的不同方面进行测试。test_name_function.py
import unittest # 导入unittest库 from name_function import get_formatted_name # 导入要测试的函数 class NamesTestCase(unittest.TestCase): """测试name_function.py""" def test_first_last_name(self): """能够正确地处理像Janis Joplin这样的姓名吗?""" formatted_name = get_formatted_name('janis', 'joplin') self.assertEqual(formatted_name, 'Janis Joplin') unittest.main()
第3行我们创建了一个名为
NamesTestCase
的类,用于包含一系列针对get_formatted_name()
的单元测试。你可随便给这个类命名,但最好让它看起来与要测试的函数相关,并包含字样Test。这个类必须继承unittest.TestCase
类,这样Python才知道如何运行你编写的测试。方法命名为
test_first_last_name()
,因为我们要核实的是只有名和姓的姓名能否被正确地格式化。我们运行 test_name_function.py 时,所有以
test
打头的方法都将自动运行。第8行里我们使用了
unittest
类最有用的功能之一:一个 断言方法 。断言方法用来核实得到的结果是否与期望的结果一致。在这里,我们知道get_formatted_name()
应返回这样的姓名,即名和姓的首字母为大写,且它们之间有一个空格,因此我们期望formatted_name的值为Janis Joplin 。为检查是否确实如此,我们调用unittest
的方法assertEqual()
,并向它传递formatted_name和’Janis Joplin’。代码行self.assertEqual(formatted_name, 'Janis Joplin')
的意思是说:“将formatted_name的值同字符串’Janis Joplin’ 进行比较,如果它们相等,就万事大吉,如果它们不相等,跟我说一声!”代码行
unittest.main()
让Python运行这个文件中的测试。运行 test_name_function.py 时,得到的输出如下:. ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
第1行的句点表明有一个测试通过了。接下来的一行指出Python运行了一个测试,消耗的时间不到0.001秒。最后的OK表明该测试用例中的所有单元测试都通过了。
而若测试不通过,会产生下面这样的输出:
E ====================================================================== ERROR: test_first_last_name (__main__.NamesTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_name_function.py", line 8, in test_first_last_name formatted_name = get_formatted_name('janis', 'joplin') TypeError: get_formatted_name() missing 1 required positional argument: 'last' ---------------------------------------------------------------------- Ran 1 test in 0.000s FAILED (errors=1)
第1行输出只有一个字母E,它指出测试用例中有一个单元测试导致了错误。
接下来,我们看到
NamesTestCase
中的test_first_last_name()
导致了错误。测试用例包含众多单元测试时,知道哪个测试未通过至关重要。在第5行,我们看到了一个标准的
traceback
,它指出函数调用get_formatted_name('janis', 'joplin')
有问题,因为它缺少一个必不可少的位置实参。我们还看到运行了一个单元测试。
最后,还看到了一条消息,它指出整个测试用例都未通过,因为运行该测试用例时发生了 1个错误 。这条消息位于输出末尾,让你一眼就能看到——你可不希望为获悉有多少测试未通过而翻阅长长的输出。
对类进行测试
Python在
unittest.TestCase
类中提供了很多断言方法。前面说过,断言方法检查你认为应该满足的条件是否确实满足。如果该条件确实满足,你对程序行为的假设就得到了确认,你就可以确信其中没有错误。如果你认为应该满足的条件实际上并不满足,Python将引发异常。import unittest from survey import AnonymousSurvey class TestAnonmyousSurvey(unittest.TestCase): """针对AnonymousSurvey类的测试""" def test_store_single_response(self): """测试单个答案会被妥善地存储""" question = "What language did you first learn to speak?" my_survey = AnonymousSurvey(question) my_survey.store_response('English') self.assertIn('English', my_survey.responses) unittest.main()
unittest Module
中的断言方法方法 用途 assertEqual(a, b) 核实a == b assertNotEqual(a, b) 核实a != b assertTrue(x) 核实x 为True assertFalse(x) 核实x 为False assertIn(item , list ) 核实 item 在 list 中 assertNotIn(item , list ) 核实 item 不在 list 中 只能在继承
unittest.TestCase
的类中使用这些方法。方法
setUp()
:如果你在
TestCase
类中包含了方法setUp()
,Python将先运行它,再运行各个以test_
打头的方法。这样,在你编写的每个测试方法中都可使用在方法setUp()
中创建的对象了。下面使用
setUp()
来创建一个调查对象和一组答案,供方法test_store_single_response()
和test_store_three_responses()
使用:import unittest from survey import AnonymousSurvey class TestAnonymousSurvey(unittest.TestCase): """针对AnonymousSurvey类的测试""" def setUp(self): """ 创建一个调查对象和一组答案,供使用的测试方法使用 """ question = "What language did you first learn to speak?" self.my_survey = AnonymousSurvey(question) self.responses = ['English', 'Spanish', 'Mandarin'] def test_store_single_response(self): """测试单个答案会被妥善地存储""" self.my_survey.store_response(self.responses[0]) self.assertIn(self.responses[0], self.my_survey.responses) def test_store_three_responses(self): """测试三个答案会被妥善地存储""" for response in self.responses: self.my_survey.store_response(response) for response in self.responses: self.assertIn(response, self.my_survey.responses) unittest.main()
运行测试用例时,每完成一个单元测试,Python都打印一个字符:测试通过时打印一个句点;测试引发错误时打印一个E;测试导致断言失败时打印一个F。