unittest笔记
背景
1. 学习资料:
官网: https://docs.python.org/2.7/library/unittest.html
https://docs.python.org/3/library/unittest.html
IBM Python自动单元测试框架: http://www.ibm.com/developerworks/cn/linux/l-pyunit/
2. python其他类似的工具
doctest: https://docs.python.org/2.7/library/doctest.html#module-doctest
unittest2: https://pypi.python.org/pypi/unittest2
nose: https://nose.readthedocs.org/en/latest/
3. 定义
unittest,有时也叫"PyUnit",是Python语言版本的单元测试框架。和JAVA语言的JUnit类似。
unittest 主要分为以下几个模块:
test fixture:
指的是测试之前需要做的准备工作以及相关的清理工作。比如,新建暂时数据库或代理数据库,目录,或者启动服务器进程。这里主要就是指 setup 以及teardown 函数。
test case(TestCase):
test case是测试的最小单元。它检查一组特定输入的结果。unittest提供一个基类,TestCase,我们可以使用TestCase来新建新的测试用例。
test suite(TestSuite):
test suite是test case的集合,也可以是test suite的集合。它用来将所有一起执行的测试集成在一起。
test runner(TestRunner):
test runner负责执行测试并且提供测试结果。runner可能使用图形接口、文字接口、或者返回一个特殊值来指定测试结果。
示例
1. 测试用例组成(TestCase)
所有的测试用例必须继承自类 unittest.TestCase ,一般测试用例方法名以 test开头。一个测试用例类内部可以定义一个或多个测试用例方法,比如下面就定义了3个方法。
import unittest class TestStringMethods(unittest.TestCase): def setUp(self): print("This is setup") def test_upper(self): print("This is test_upper") self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): print("This is test_isupper") self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): print("This is test_split") 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) def tearDown(self): print("This is tearDown") if __name__ == '__main__': unittest.main()
脚本上右键,选择 Run -> Run as Python unit-test ,就可以看到控制台输出如下:
Finding files... done. Importing test modules ... done. This is setup This is test_isupper This is tearDown This is setup This is test_split This is tearDown This is setup This is test_upper This is tearDown ---------------------------------------------------------------------- Ran 3 tests in 0.004s OK
可以看到,每个测试用例方法执行之前都会调用 setUp方法,每个测试用例方法执行结束都会调用 tearDown方法。
一般会把测试用例执行前的准备工作放到setUp方法里面,比如WEB测试的登录操作。setUp执行时如果抛出异常,后边的test都不会被执行,该用例会被标记为error。
一般会把测试用例执行后的清理工作放到tearDown方法里面,比如WEB测试的关闭浏览器操作。只要setUp执行成功,tearDown就会被执行。tearDown抛出异常的话,测试结果会被标记为error。
2. 测试结果判定
unittest.TestCase 类包含各种判定测试结果的方法。
比如上面我们使用过的“self.assertEqual('foo'.upper(), 'FOO')”。
Method | Checks that | New in |
---|---|---|
assertEqual(a, b) |
a == b |
|
assertNotEqual(a, b) |
a != b |
|
assertTrue(x) |
bool(x) is True |
|
assertFalse(x) |
bool(x) is False |
|
assertIs(a, b) |
a is b |
3.1 |
assertIsNot(a, b) |
a is not b |
3.1 |
assertIsNone(x) |
x is None |
3.1 |
assertIsNotNone(x) |
x is not None |
3.1 |
assertIn(a, b) |
a in b |
3.1 |
assertNotIn(a, b) |
a not in b |
3.1 |
assertIsInstance(a, b) |
isinstance(a, b) |
3.2 |
assertNotIsInstance(a, b) |
not isinstance(a, b) |
3.2 |
所有的assert方法都含有一个默认参数“msg”,该参数默认值为“None”。如果给msg传值,在assert判定fail的时候,系统就会打印出msg作为fail message。
“self.assertEqual('foo'.upper(), 'FOO1',"foo.upper is not FOO1!")”
如果assert方法判定特定条件不满足,就会抛出一个异常 AssertionError,unittest执行结果就会被判定为 fail。
3. 测试用例集(TestSuite)
多个TestCase可以组成一个TestSuite 。一般实际测试都有很多个TestCase,我们需要对其进行组合,这样方便执行以及收集结果。
跑一个TestSuite ,和单独跑TestSuite里面的所有的TestCase ,结果是一样的。
多个TestSuite 也可以组成一个更大的TestSuite。
import unittest class TestStringMethods(unittest.TestCase): # 类内容参见上面的代码 def suite(): suite = unittest.TestSuite() suite.addTest(TestStringMethods('test_upper')) suite.addTest(TestStringMethods('test_split')) return suite if __name__ == '__main__': print(suite())
执行结果如下:
<unittest.suite.TestSuite tests=[<__main__.TestStringMethods testMethod=test_upper>,
<__main__.TestStringMethods testMethod=test_split>]>
可以看到suite()返回的是一个 TestSuite实例,里面只含有我们添加进去的测试用例方法。
TestSuite 主要包含4个方法:
addTest(test):添加test到TestSuite。这里的test可以是一个TestCase,也可以是一个TestSuite。
addTests(tests):添加多个test到TestSuite。这里的tests 可以是一个 TestCase列表,也可以是一个TestSuite列表。
run(result):跑TestSuite,并且收集测试结果到result。
countTestCases():返回该TestSuite说包含的所有的TestCase数目。
4. TestLoader
该类的目的就是为了更方便的整合所有testCase 为 TestSuite。
一般都不需要实例化一个TestLoader对象,可以直接通过 unittest.defaultTestLoader 来获得一个 TestLoader 实例。
该类主要包含的都是loadTest方法,就是通过各种方式来收集TestCase 。 方法返回的是一个TestSuite 实例 。
loadTestsFromTestCase(testCaseClass):通过TestCase的类名加载用例。比如 TestStringMethods 。
loadTestsFromModule(module, pattern=None):通过模块名加载用例。
loadTestsFromName(name, module=None):通过名称加载用例。该名称一般是“xxx.xxxx.xxx.xxx”的形式,可以具体到TestCase类名(比如 TestStringMethods),也可以具体到类中的方法名(比如 test_split )。
loadTestsFromNames(names, module=None):通过多个名称加载用例。类似 loadTestsFromName ,只不过这里的参数是一个集合,比如一个列表。
discover(start_dir, pattern='test*.py', top_level_dir=None):通过路径加载用例。 start_dir 一般是一个大的路径。pattern一般是具体脚本的匹配模式,默认是所有以test开头的脚本。
使用示例:
import unittest class TestStringMethods(unittest.TestCase): #类内容参见上面的代码 def suite(): suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestStringMethods) return suite if __name__ == '__main__': print(suite())
输出结果如下:
<unittest.suite.TestSuite tests=[<__main__.TestStringMethods testMethod=test_isupper>,
<__main__.TestStringMethods testMethod=test_split>,
<__main__.TestStringMethods testMethod=test_upper>]>
可以看到返回的是一个TestSuite实例。
5. TestResult
该类用来收集测试结果。所有的测试结果都可以通过TestResult获取。
import unittest class TestStringMethods(unittest.TestCase): #类内容参见上面的代码 def suite(): suite =unittest.defaultTestLoader.loadTestsFromTestCase(TestStringMethods) return suite if __name__ == '__main__': my_result=unittest.TestResult() my_suite=suite() my_suite.run(my_result) print("My Result:",my_result) print("Errors:",my_result.errors) print("Failures:",my_result.failures) print("Total Run case:",my_result.testsRun)
输出结果:
My Result: <unittest.result.TestResult run=3 errors=0 failures=1> Errors: [] Failures: [(<__main__.TestStringMethods testMethod=test_upper>, 'Traceback (most recent call last):\n File "E:\\workspace\\Test\\src\\test\\test_unittest.py", line 10, in test_upper\n self.assertEqual(\'foo\'.upper(), \'FOO1\',"oooFail")\nAssertionError: \'FOO\' != \'FOO1\'\n- FOO\n+ FOO1\n? +\n : oooFail\n')]
Total Run case: 3
errors : 返回一个列表。如果测试Pass,则列表为空。不然就是所有的error集合。
failures : 返回一个列表。如果测试Pass,则列表为空。不然就是所有的failure集合。比如上面我故意更改 test_upper的assert判定条件,导致该用例fail。这里就记录了failure的情况。
testsRun:返回一个整数。值为所有的run的测试用例数目。比如这里就是3。
TestResult 还含有一些方法,感觉主要作用就是定义自己的TestResult时候可以重写这些方法。
startTest(test) :将要执行测试用例方法的时候调用。
stopTest(test) :测试用例方法执行完毕后调用。
startTestRun() :执行第一个用例前调用。
stopTestRun() :执行最后一个用例后调用。
addError(test, err) :测试用例返回error时调用。
addFailure(test, err) :测试用例返回fail时调用。
addSuccess(test) :测试用例返回pass时调用。
6. 命令行执行
示例:
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
可以采用 “python -m unittest xxxx” 的方式来执行测试用例。 xxxx可以是模块、用例类、用例类中的方法、或者文件路径。
>python -m unittest test_unittest.py ... ---------------------------------------------------------------------- Ran 3 tests in 0.001s OK
如果想获取更详细的内容,可以加 -v 。
>python -m unittest -v test_unittest.py test_isupper (tet_excel.TestStringMethods) ... ok test_split (tet_excel.TestStringMethods) ... ok test_upper (tet_excel.TestStringMethods) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.002s OK
也可以使用discover来自动加载测试用例。但是前提是脚本名称必须以 test开头。
discover 命令还有其他的一些使用方法,比如自定义匹配模式等等。这里不细说。
>cd project_directory >python -m unittest discover ... ---------------------------------------------------------------------- Ran 3 tests in 0.001s OK
7. 跳过执行
如果某个测试方法不想执行,给它加上 skip()装饰器即可。
示例:
import unittest import sys from common import mylib class MyTestCase(unittest.TestCase): @unittest.skip("demonstrating skipping") def test_nothing(self): self.fail("shouldn't happen") @unittest.skipIf(mylib.__version__< 5, "not supported in this library version") def test_format(self): # Tests that work for only a certain version of the library. pass @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_windows_support(self): # windows specific testing code pass if __name__ == '__main__': unittest.main(verbosity=2)
执行结果如下:
test_format (__main__.MyTestCase) ... skipped 'not supported in this library version' test_nothing (__main__.MyTestCase) ... skipped 'demonstrating skipping' test_windows_support (__main__.MyTestCase) ... ok ---------------------------------------------------------------------- Ran 3 tests in 0.001s OK (skipped=2)
可以看到前面两个用例都被跳过了。但是因为我的环境时window的,所以第3个用例没有被跳过。
如果某个测试用例类不想执行,也可以跳过。
import unittest @unittest.skip("showing class skipping") class MySkippedTestCase(unittest.TestCase): def test_not_run(self): pass def test_not_run2(self): pass if __name__ == '__main__': unittest.main(verbosity=2)
执行结果如下:
test_not_run (__main__.MySkippedTestCase) ... skipped 'showing class skipping' test_not_run2 (__main__.MySkippedTestCase) ... skipped 'showing class skipping' ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK (skipped=2)
可以看到两个测试用例方法都被跳过了。
8. TestProgram
关于命令行执行的一个测试类。没看到官方文档有详细的资料,暂时不研究。