一、Unittest 的几个基本概念
- TestCase :要写的具体的测试用例
- TestSuite: 多个测试用例集合(或测试套件/测试集)
- TestLoader:用来加载 TestCase 到 TestSuite中的(更通俗一点,就是用来把符合我们定义的条件的测试用例组合起来,成为一个测试集),一般会以参数的形式传进去一些条件
- TestRunner:是来执行测试用例的,测试的结果会保存到 TestResult 实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息
二、简单小示例
import unittest
class MyTest(unittest.TestCase):
@classmethod
def setUpClass(self):
"""测试用例执行之前执行"""
print("UnitTest Begin...")
@classmethod
def tearDownClass(self):
"""测试用例执行之后执行"""
print("UnitTest End...")
def setUp(self):
print("Begin...")
def tearDown(self):
print("End...")
def test_1(self):
self.assertEqual(1, 1)
def test_2(self):
self.assertEqual(1, 2)
if __name__ == '__main__':
unittest.main()
运行结果:
UnitTest Begin...Begin...
End...
Begin...
End...
Ran 2 tests in 0.009s
FAILED (failures=1)
Failure
Traceback (most recent call last):
File "C:\Users\hj\Anaconda3\Lib\unittest\case.py", line 59, in testPartExecutor
yield
File "C:\Users\hj\Anaconda3\Lib\unittest\case.py", line 605, in run
testMethod()
File "E:\Python_virtualenvs\for_django\Projects\测试模块\1、小示例.py", line 23, in test_2
self.assertEqual(1, 2)
File "C:\Users\hj\Anaconda3\Lib\unittest\case.py", line 829, in assertEqual
assertion_func(first, second, msg=msg)
File "C:\Users\hj\Anaconda3\Lib\unittest\case.py", line 822, in _baseAssertEqual
raise self.failureException(msg)
AssertionError: 1 != 2
测试发现有一个错误:1 != 2
。
参数说明
setUp()
和tearDown()
:测试用例执行之前和执行之后执行,一般用来做测试准备工作,如:连接(关闭)数据库、Web 登录等- 类方法
@classmethod
:类在未实例化之前调用类方法将会报错:TypeError: setUpClass() missing 1 required positional argument: 'self'
test_1()
和test_2()
:具体的测试用例,函数名一般以test
开头(更为规范)self.assertEqual()
:断言方法,用于判断测试用例是否能通过
三、常用断言方法
断言方法返回值为布尔值,如果指定msg参数的值,则将该信息作为失败的错误信息返回:
断言方法 | 断言描述 | 断言方法 | |
---|---|---|---|
assertEqual(arg1, arg2, msg=None) | 验证 arg1 = arg2 | assertNotEqual(arg1, agr2, msg=None) | 验证 arg1 != arg2 |
assertTrue(expr, msg=None) | 验证 expr 表达式为 True | assertFalse(expr, msg=None) | 验证 expr 为 False |
assertIs(arg1, arg2, msg=None) | 验证 arg1 与 arg2 是同一对象 | assertIsNot(arg1, arg2, msg=None) | 验证 arg1 和 arg2 不是同一对象 |
assertIn(arg1, arg2, msg=None) | 验证 arg1 是 arg2 的子串 | assertNotIn(arg1, arg2, msg=None) | 验证 arg1 不是 arg2 的子串 |
assertIsInstance(obj, cls, msg=None) | 验证 obj 是 cls 的实例 | assertNotIsInstance(arg1, arg2, msg=None) | 验证 obj 不是 cls 的实例 |
如果程序要对异常、错误、警告和日志进行断言判断,TestCase
提供了如下所示的断言方法:
断言方法 | 检查条件 |
---|---|
assertRaises(exc, fun, *args, **kwds) | fun(*args, **kwds) 引发 exc 异常 |
assertRaisesRegex(exc, r, fun, *args, **kwds) | fun(*args, **kwds) 引发 exc 异常,且异常信息匹配 r 正则表达式 |
assertWarns(warn, fun, *args, **kwds) | fun(*args, **kwds) 引发 warn 警告 |
assertWamsRegex(warn, r, fun, *args, **kwds) | fun(*args, **kwds) 引发 warn 警告,且警告信息匹配 r 正则表达式 |
assertLogs(logger, level) | With 语句块使用日志器生成 level 级别的日志 |
示例:
def func(a, b):
"""一元一次方程"""
if a == 0:
raise ValueError('a 参数错误,不能为 0')
else:
return b // a
import unittest
class Test(unittest.TestCase):
def test_fuc(self):
self.assertEqual(func(2, 6), 3)
# 检查 a = 0 时会不会引发 ValueError
with self.assertRaises( ):
func(0, 9)
特定检查的断言方法:
断言方法 | 检查条件 |
---|---|
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() 判断两个对象是否相等时,如果被判断的类型是字符串、序列、列表、元组、集合、字典,则程序会自动改为使用如下的断言方法进行判断。换而言之,下表断言方法其实没有必要使用,unittest 模块会自动应用它们:
断言方法 | 用于比较的类型 |
---|---|
assertMultiLineEqual(a, b) | 字符串(string) |
assertSequenceEqual(a, b) | 序列(sequence) |
assertListEqual(a, b) | 列表(list) |
assertTupleEqual(a, b) | 元组(tuple) |
assertSetEqual(a, b) | 集合(set 或 frozenset) |
assertDictEqual(a, b) | 字典(dict) |
运行测试的两种方法
1、调用 unittest.main()
来运行当前源文件中的所有测试用例
if __name__ == '__main__':
unittest.main()
2、使用 unittest
模块运行测试用例
python -m unittest 测试文件
测试结果说明:
Ran 1 test in 0.002s
OK
- .:代表测试通过。
- F:代表测试失败,F 代表 failure。
- E:代表测试出错,E 代表 error。
- s:代表跳过该测试,s 代表 skip。
四、常见测试写法
方法一
搜索该模块下所有以 test 开头的测试用例方法,并自动执行它们:
import unittest
class Test(unittest.TestCase):
def setUp(self):
self.num = input('请输入一个数字:')
self.num = int(self.num)
print('测试开始')
def test_case1(self):
print(self.num)
self.assertEqual(self.num, 10, msg='你输入的不是 10')
def test_case2(self):
print(self.num)
self.assertEqual(self.num + 10, 20, msg='你输入的不是 20')
@unittest.skip('暂时跳过用例3的测试')
def test_case3(self):
print(self.num)
self.assertNotEqual(self.num, 30, msg='你输入的不是 30')
def tearDown(self):
print('测试结束')
if __name__ == '__main__':
unittest.main()
五、TestSuite测试包及用法
测试包(TestSuite)可以组织多个测试用例,组建好测试包后再用测试运行器(TestRunner)来运行该测试包所包含的所有测试用例。
下面来组建一个测试包,包含两个测试用例:
1、s1.py
def func(a, b):
"""一元一次方程"""
if a == 0:
raise ValueError('a 参数错误,不能为 0')
else:
return b // a
2、t1.py
import unittest
from s1 import *
class Test(unittest.TestCase):
def test_fuc(self):
self.assertEqual(func(2, 6), 3)
with self.assertRaises(ValueError):
func(0, 9)
3、s2.py
def say_hello():
return 'Hello'
4、t2.py
import unittest
from s2 import *
class TestHello(unittest.TestCase):
def test_say_hello(self):
self.assertEqual(say_hello(), 'Hello')
我们将在 t3.py 中组建一个测试包,同时测试 t1、t2:
# t3.py
import unittest
from t1 import Test
from t2 import TestHello
test_case = (Test, TestHello)
def build_suite():
# 创建测试加载器
loader = unittest.TestLoader()
# 创建测试包
suite = unittest.TestSuite()
# 遍历所有测试类
for test_class in test_case:
# 从测试类中加载测试用例
tests = loader.loadTestsFromTestCase(test_class)
# 将测试用例添加到测验包中
suite.addTests(tests)
return suite
if __name__ == '__main__':
# 创建测试运行器 (TestRunner)
runner = unittest.TextTestRunner(verbosity=2) # verbosity=2 测试结果显示详细信息
runner.run(build_suite())
运行结果如下:
test_fuc (t1.Test) ... ok
test_say_hello (t2.TestHello) ... ok
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
如果希望直接生成文件格式的测试报告,可 指定 TextTestRunner
对象的 stream
属性:
if __name__ == '__main__':
with open('abc.txt', 'a') as f:
# 创建测试运行器(TestRunner),将测试报告输出到文件中
runner = unittest.TextTestRunner(verbosity=2, stream=f)
runner.run(build_suite())