Python unittest
Python unittest
unittest 是Python 自带的单元测试框架,用它来做单元测试,它里面封装好了一些校验返回的结果方法和一些用例运行前的初始化操作。
官方文档:https://docs.python.org/3/library/unittest.html#module-unittest
简介
unittest 中有一些类:
TestCase 测试用例
TestSuite 多个测试用例集合在一起,就是TestSuite
TestLoader 用来加载 TestCase 到 TestSuite 中的
TestRunner 是来运行测试用例的, 测试的结果会保存到 TestResult 实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息
使用
unittest 要求单元测试类必须继承 unittest.TestCase,该类中的测试方法需要满足如下要求:
- 没有返回值
- 不应该有任何参数
- 应以test 开头
示例:
import unittest
class TestFunc(unittest.TestCase):
def test_1(self):
self.assertEqual(func1(1, 1), 2)
def test_2(self):
self.assertEqual(func1(1, 2), 3)
def func1(a, b):
return a + b
if __name__ == '__main__':
unittest.main()
返回结果:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
断言方法
常用的方法:
断言方法 | 检查条件 |
---|---|
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 |
assertIsNot(a, b) | a is not b |
assertIsNone(x) | x is None |
assertIsNotNone(x) | x is not None |
assertIn(a, b) | a in b |
assertNotIn(a, b) | a not in b |
assertlsInstance(a, b) | isinstance(a, b) |
assertNotIsInstance(a, b) | not isinstance(a, b) |
包含检查 exceptions, warnings, and log messages 的方法:
断言方法 | 检查条件 |
---|---|
assertRaises(exc, fun, \*args, \*\*kwds) | fun(\*args, \*\*kwds) raises exc 异常 |
assertRaisesRegex(exc, r, fun, \*args, \*\*kwds) | fun(\*args, \*\*kwds) raises exc 异常,且异常信息匹配正则表达式 r |
assertWarns(warn, fun, \*args, \*\*kwds) | fun(\*args, \*\*kwds) raises warn 警告 |
assertWarnsRegex(warn, r, fun, \*args, \*\*kwds) | fun(\*args, \*\*kwds) raises warn 警告,且异常信息匹配正则表达式 r |
assertLogs(logger, level) | with 语句块使用日志器 (logger) 生成 level 级别的日志 |
包含特定检查的方法:
断言方法 | 检查条件 |
---|---|
assertAlmostEqual(a, b) | round(a-b, 7) == 0 |
assertNotAlmostEqual(a, b) | round(a-b, 7) != 0 |
assertGreater(a, b) | a > b |
assertGreaterEqual(a, b) | a >= b |
assertLess(a, b) | a < b |
assertLessEqual(a, b) | a <= b |
assertRegex(s, r) | r.search(s) |
assertNotRegex(s, r) | not r.search(s) |
assertCountEqual(a, b) | a、b 包含的元素及其数量相同,无视顺序 等效于`assertEqual(Counter(list(first)), Counter(list(second)))` |
返回结果
第一行表示每个测试用例的运行结果:
从第一个开始表示相应顺序的测试用例运行结果
.
: 测试通过
F
: FAILURE 测试失败
E
: ERROR 测试出错
S
: Skip 跳过该测试
之后是错误信息 (如果有的话):
示例:
======================================================================
ERROR: test_1 (__main__.TestFunc)
----------------------------------------------------------------------
Traceback (most recent call last):
File "test.py", line 30, in func1
raise IndexError
IndexError
======================================================================
FAIL: test_2 (__main__.TestFunc)
----------------------------------------------------------------------
Traceback (most recent call last):
File "text.py", line 27, in test_2
self.assertEqual(func1(1, 2), 4)
AssertionError: 3 != 4
如果是测试出错,会将错误显示出来
如果是测试失败,会将失败原因以及函数运行的实际结果和期望结果的差异显示出来
最后是所有测试用例的运行结果:
示例:
----------------------------------------------------------------------
Ran 3 tests in 0.000s
FAILED (failures=1, errors=1)
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
会显示本次测试一共运行了几个测试用例,以及运行时间
最后是运行的结果
OK
: 所有测试用例均通过
FAILED
: 没有全部通过 (以及失败数量和错误数量)
运行顺序
在 TestCase 中有一些特殊的方法:
setUp
: 每个测试用例之前运行
tearDown
: 每个测试用例之后运行
setUpClass
: 所有测试用例之前 (setUp) 运行
tearDownClass
: 所有测试用例之后 (tearDown) 运行
运行顺序示例:
import unittest
class TestFunc(unittest.TestCase):
def tearDown(self) -> None:
print('from tearDown')
def setUp(self) -> None:
print('from setUp')
@classmethod
def tearDownClass(cls) -> None:
print('from tearDownClass')
@classmethod
def setUpClass(cls) -> None:
print('from setUpClass')
def test_1(self):
self.assertEqual(func1(1, 1), 2)
def test_2(self):
self.assertEqual(func1(1, 2), 3)
def func1(a, b):
return a + b
if __name__ == '__main__':
unittest.main()
返回结果
from setUpClass
..
from setUp
----------------------------------------------------------------------
from tearDown
Ran 2 tests in 0.000s
from setUp
from tearDown
from tearDownClass
OK
运行多个测试用例
使用 unittest.main()
会直接运行所有的测试用例,如果希望只运行一些特定的测试用例,就需要用到 TestSuite
类
TestSuite
是许多测试用例的集合,通过 addTest()
方法将测试用例添加到 TestSuite
中
之后实例化一个 TextTestRunner(TestRunner)
类,最后调用 run()
就可以运行测试用例了
示例:
import unittest
from typing import Any
# import HTMLTestRunner
class MyTestFunc1(unittest.TestCase):
def test_1(self):
print('from Func1 test_1')
self.assertEqual(func1(1, 1), 2)
def test_2(self):
print('from Func1 test_2')
self.assertNotEqual(func1(1, 2), 4)
class MyTestFunc2(unittest.TestCase):
def test_1(self):
print('from Func2 test_1')
self.assertTrue(func1(1, 2))
def func1(a, b):
return a + b
if __name__ == '__main__':
test_suite = unittest.TestSuite()
test_suite.addTest(unittest.makeSuite(MyTestFunc2))
test_suite.addTest(MyTestFunc1('test_1'))
runner = unittest.TextTestRunner()
runner.run(test_suite)
返回结果:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
from Func2 test_1
from Func1 test_1
可以看出只有 MyTestFunc1 中的 test_1 和 MyTestFunc2 中的 test_1 运行了
并且运行顺序取决于添加到 TestSuite 中的顺序
获取报告
通过 TextTestRunner
类可以将测试结果储存到文件中,而不是直接打印出来,只需要实例化时传入相关参数即可
f = open('report.txt', 'w')
runner = unittest.TextTestRunner(stream=f)
还有一些其他的参数,可以在 TextTestRunner
类源码中看到
其他模块
通过其他模块可以获取其他格式的测试结果
这些模块需要单独安装 HTMLTestRunner
、xmlrunner
使用方法相似,实例化时传入必要的参数,之后调用 run()
方法即可