起步
随着项目不断变得庞大,复杂性越来越高。为了保证代码质量和可用性,可以将应用的最小部件来进行正确性的检测工作。因此就有了单元测试。单元测试带来了诸多的好处:提高代码质量;提高程序的健壮性;避免代码重构引入新的问题。
unittest 作为内置的标准库,它很优秀,由于受到 JUnit 的启发,因此它与其他语言单元测试的风格类似。
测试文件代码结构
官方文档:https://docs.python.org/zh-cn/3.9/library/unittest.html,以下是官方的例子,我们可以从中学会一个测试文件的代码结构:
import unittest class TestStringMethods(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): s = 'hello world' self.assertEqual(s.split(), ['hello', 'world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): s.split(2) if __name__ == '__main__': unittest.main() |
如上面所示的,测试类需要继承 unittest.TestCase 类,需要进行测试的方法的函数名应该 test 开头,不以 test 开头的方法测试的时候不会被主动调用。测试的正确性关键是调用 assert..() 来检查预期的结果。框架提供了诸多的 assertxxx() 函数组,其中最常用的有三个:
· assertEqual :用于判断两个值是否相等;
· assertTrue/assertFalse 用于判断表达式是 True 还是 False;
· assertRaises :用于检测异常。
运行测试
如果测试文件用有 unittest.main() ,那么它可以安装普通py文件一样命令行运行, eg: python filename.py :
... ----------------------------------- Ran 3 tests in 0.000s OK |
若在调用命令行运行时添加 -v 参数,则显示的信息会更详细, eg: python filename.py -v:
test_isupper (__main__.TestStringMethods) ... ok test_split (__main__.TestStringMethods) ... ok test_upper (__main__.TestStringMethods) ... ok ----------------------------------- Ran 3 tests in 0.001s OK |
如果测试文件没有提供 unittest.main() ,那么可以采用 -m unittest 来执行要测试的模块,eg: python -m unittest filename 进行。
python -m unittest test_module1 test_module2 # 测试多个模块 python -m unittest test_module.TestClass python -m unittest test_module.TestClass.test_method # 测试指定方法 python -m unittest tests/test_something.py # 指定路径 |
前置(setUp)和清理(tearDown)方法。
setUp() 是在执行测试方法前的前置操作,用来完成测试前的准备工作,比如建立数据库连接,打开文件等。tearDown() 则是在测试方法都执行完毕后用来清理工作的,比如断开数据库连接,关闭文件等。
import unittest class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget('The widget') def tearDown(self): self.widget.dispose() |
若在 setUp() 方法中发生了异常,测试框架视为测试发生了错误,测试方法不会被执行。
若 setUp() 运行成功,无论测试方法是否成功,都会运行 tearDown() 。
跳过测试
如果想跳过某测试用例暂,可以在该方法前加一个装饰器:
class MyTestCase(unittest.TestCase): @unittest.skip("demonstrating skipping") def test_nothing(self): self.fail("shouldn't happen") |
有了这个装饰器,就可以针对不同的场景,不同的平台进行特定的测试:
# 针对依赖的版本测试 @unittest.skipIf(mylib.__version__ < (1, 3), "not supported in this library version") def test_format(self): # Tests that work for only a certain version of the library. pass # 针对win平台进行的测试 @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_windows_support(self): # windows specific testing code pass |
跳过的用例在终端中输出:
test_format (__main__.MyTestCase) ... skipped 'not supported in this library version' test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping' test_maybe_skipped (__main__.MyTestCase) ... skipped 'external resource not available' test_windows_support (__main__.MyTestCase) ... skipped 'requires Windows' ---------------------------------------------------------------------- Ran 4 tests in 0.005s OK (skipped=4) |
可以跳过整个测试类:
@unittest.skip("showing class skipping") class MySkippedTestCase(unittest.TestCase): def test_not_run(self): pass |
使用 expectedFailure() 表明预期失败,这个常用在程序有已知的问题,但还找不到解决方案,就将测试用例写在单元测试中,以后再来解决这些问题:
class ExpectedFailureTestCase(unittest.TestCase): @unittest.expectedFailure def test_fail(self): self.assertEqual(1, 0, "broken") |
总结
unittest 使用简单,功能强大,日常的测试需求都能得到很好的满足。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。