Unittest01|TestCase、断言、装饰器、夹具、清理函数、ddt
一、入门
1、新建测试文件
打开pycharm→左上角新建项目→选择项目路径→选择python解释器→创建→点击新建好的项目,右键新建python文件→测试文件(py文件)命名必须以test开头
2、创建测试用例
定义测试类:导入unittest模块→创建一个测试类,并且继承unittest中的TestCase类
定义测试方法:就是python中的实例方法,必须以test开头
3、运行测试文件
①在py文件右键运行文件
②pycharm对自带的测试运行可视化按钮
③命令行:在pycharm终端输入
python 文件名.py
4、入门代码
import unittestclass TestDemo(unittest.TestCase):def test_01_login(self): # 1、这测试方法名字也必须用test开头;2、这是python中的实例方法,self指向当前实例对象。print("------这是测试登录的用例------")def test_02_register(self):print("------这是测试注册的用例------")if __name__ == "__main__": # 判断是否直接运行的本文件,如果直接运行的本文件才会运行unittest.main(),因为本文件可能会被作为模块被其他文件导入unittest.main() # unittest.main() 会自动发现和运行当前模块中所有的测试用例。它会搜索文件中所有符合 test* 命名规则的测试方法(如 test_example),并执行它们,报告测试结果。
'''
__name__ 是一个特殊的变量,它表示当前模块的名称。'''
二、使用断言实现对测试用例结果的判断
1、断言
待补充
三、通过装饰器控制测试用例是否需要执行
TestCase自带很多不同的装饰器,实现控制我们测试用例的运行过程。
在实际的工作中,用例可能存在很多,但是这些用例有可能不是所有的用例都需要被运行,有的用例需要根据不同的情况来判断是否需要运行。利用TestCase自带的装饰器来实现用例运行的过程的控制。
【补充】unittest运行结果的说明:
. 表示测试通过;F表示测试失败;s表示跳过了,没有参数测试;x表示预期失败;u表示意外成功。
四、测试夹具在测试用例中的使用
夹具(Fixture)的作用可以把我们测试运行前的设置测试环境和运行结束后的清理工作相关的代码抽离出来,单独放到夹具中区运行。夹具主要用于准备一些必要的资源(如数据库连接、文件、网络服务等),可以方便代码的组织和维护,减少重复的代码。
先运行 初始化的夹具(setUp或setUpClass或setUpModule),再运行 测试用例,最后运行 测试运行结束后的夹具(tearDown或tearDownClass或earDownModule)。初始化的夹具 和 测试运行结束后的夹具 不一定要成对出现,其中哪一个都可以单独使用。
unittest 在运行测试时的执行流程:
①测试方法开始执行:首先运行测试方法。
②setUp() 被调用:在每个测试方法执行前,setUp() 被调用,用于初始化测试环境。
③执行测试代码:测试方法的主体代码被执行。
④tearDown() 被调用:测试方法执行后,tearDown() 被调用,用于清理测试环境。
⑤显示测试结果:在 tearDown() 完成后,测试框架会显示结果,如 .、F 或 E,以表示测试的成功、失败或错误。
⑥setUpClass() 和 tearDownClass():如果使用了类级别的夹具 ,它们分别会在所有测试方法运行前后各调用一次,通常会用于资源初始化和清理。
⑦setUpModule() 和 tearDownModule():如果使用了模块级别的夹具它们分别会在所有测试该模块类运行前后各调用一次。
【模块】通常就是一个.py文件
1、setUp()
setUp() 方法在每个测试方法执行之前被调用。它用于创建或初始化测试所需的资源。
如果某些资源或状态在多个测试方法中共享,你可以在 setUp() 中进行初始化。
2、tearDown()
tearDown() 方法在每个测试方法执行之后被调用。它用于清理测试过程中的副作用,如关闭文件、数据库连接、清理缓存等。
3、setUpClass() 和 tearDownClass()
setUpClass() 和 tearDownClass() 是类级别的夹具方法,用于在整个测试类的所有测试方法之前和之后执行一次操作。
这两个方法是类方法,需要使用 @classmethod 装饰器。
4、setUpModule() 和 tearDownModule()
setUpModule() 和 tearDownModule() 用于模块级别的夹具,在模块内的所有测试类之前和之后分别执行一次操作。
5、代码示例
test_04.py
import unittest
# 模块级别夹具
def setUpModule():print("setUpModule: 在整个模块开始之前执行一次,进行模块级别初始化")def tearDownModule():print("tearDownModule: 在整个模块结束之后执行一次,进行模块级别的清理")class MyTestCase(unittest.TestCase):# 类级别夹具@classmethoddef setUpClass(cls):print("setUpClass: 在所有测试方法之前调用一次,进行类级别的初始化")@classmethoddef tearDownClass(cls):print("tearDownClass: 在所有测试方法之后调用一次,进行类级别的清理")# 方法级别夹具def setUp(self):print("setUp: 在每个测试方法之前调用,进行方法级别的初始化")self.test_data = {"name": "John", "age": 30}def tearDown(self):print("tearDown: 在每个测试方法之后调用,进行方法级别的清理")self.test_data = Nonedef test_name(self):print("测试 test_name")# self.assertEqual(self.test_data["name"], "John")self.assertEqual(1,1)def test_age(self):print("测试 test_age")#self.assertEqual(self.test_data["age"], 30)self.assertEqual(1, 1)if __name__ == "__main__":unittest.main()
使用命令行 运行测试文件:python test_04.py 后的结果
五、清理函数让测试用例更加自由
清理函数addCleanup和doCleanups是python3.1之后新加的功能。
在 unittest 中,清理函数默认是在teardown后面运行。
清理函数的使用,需要先自己封装一个方法,这个方法写的代码用来起清理作用。
我们可以在测试类的任意位置,注册清理函数。
如果addCleanup写在了setUp中,那么这个清理函数会对所有用例生效。
self.addCleanup()
我们可以用过doCleanups来控制清理函数运行的位置。
在python3.9后新增了addClassCleanup和doClassCleanups两个方法,这两个方法是针对测试类来运行的。
【总结】当我们在setUp中使用了addCleanup,那么默认会在每条用例的teardown后运行addCleanup,如果addCleanup写在了用例中,那么就只会在这条用例的teardown后运行addCleanup。也可以自定义doCleanups运行的位置。
清理函数不是必须和夹具配合使用的,也可以单独使用。
【清理函数和夹具的区别】
相同点:都可以说实现对用例、测试类前置和后置的操作处理;
不同点:清理函数可以自定义使用的位置,可以选择在个别的用例上使用清理函数。
六、使用subTest实现数据驱动测试
subTest() 是一个用于运行多个子测试的机制,允许在同一个测试方法中执行多个不同的断言,而每个断言都被视为独立的测试。如果其中某个子测试失败了,它不会影响其他子测试的执行,这使得你能够在一次测试运行中更高效地处理多个情况。
import unittestclass MyTestCase(unittest.TestCase):def test_numbers(self):numbers = [2, 3, 4, 5, 6]for num in numbers:with self.subTest(num=num):self.assertEqual(num % 2, 0, f"{num} is not even")if __name__ == "__main__":unittest.main()
七、使用ddt实现数据驱动测试(更常用)
ddt 是一个第三方库(全称为 "Data-Driven Tests"),用于简化在 unittest 框架中进行数据驱动测试的过程。灵活地为测试方法传递多个不同的数据集,并让每个数据集对应一个子测试。ddt 通过装饰器的方式提供了这一功能,它使得你可以使用不同的数据集合来运行同一个测试方法。
- 下载包
pip install ddt -i https://pypi.tuna.tsinghua.edu.cn/simple
- 导入包
from ddt import ddt,data,unpack,file_data # 全是装饰器
1. 使用 @ddt 、 @data 、@unpack装饰器
- @ddt:装饰测试类,表示整个类中的测试方法使用数据驱动测试。
- @data:装饰测试方法,手动提供一组数据,告诉测试方法(也就是测试用例)测试数据是什么。其中,@data 装饰器是以 元组、列表、字典 或其他数据结构为单位来为测试方法传递数据的。
- @unpack:用来解包复杂的数据结构(如元组或列表),将其拆开,传递给测试方法的参数。这样,你可以将数据集中的每个元素传递给测试方法作为单独的参数,而不是作为一个整体。
1️⃣使用元组或字典为数据源
import unittest
from ddt import ddt, data, unpack# 使用 ddt 装饰器装饰测试类
@ddt
class TestAddition(unittest.TestCase):# 使用 data 装饰器提供不同的测试数据# 这里data里也可以传入三个列表,效果一样 # @data([1, 2, 3], [-1, 1, 0], [100, 200, 300]) @data((1, 2, 3), (-1, 1, 0), (100, 200, 300)) #@data 中传递了三个元组 (1, 2, 3), (-1, 1, 0), (100, 200, 300)。@unpack # 自动将每个元组解包为三个单独的参数def test_add(self, a, b, expected):print(f"Testing {a} + {b} = {expected}")self.assertEqual(a + b, expected)if __name__ == "__main__":unittest.main()
2️⃣使用字典作为数据源
当 @data 装饰器接受 字典 数据时,它会将字典本身(键 和 值 作为整体)作为一个参数传递给测试方法。字典的每对键值对会存储在测试方法的参数中,如果你使用了 @unpack 装饰器,@unpack会将字典的每个 键值对 解包成独立的参数传递给测试方法。这意味着字典的 键 会被当作参数名,值 会成为相应的参数值。
import unittest
from ddt import ddt, data, unpack@ddt
class TestAddition(unittest.TestCase):# 传递字典数据@data({'a': 1, 'b': 2, 'expected': 3}, {'a': -1, 'b': 1, 'expected': 0})@unpack # 使用 unpack 来解包字典def test_add(self, a, b, expected):print(f"Testing {a} + {b} = {expected}")self.assertEqual(a + b, expected)if __name__ == "__main__":unittest.main()# @data 装饰器传递了两个字典:{'a': 1, 'b': 2, 'expected': 3} 和 {'a': -1, 'b': 1, 'expected': 0}。
# @unpack 装饰器会将字典中的 键值对 解包,并将它们分别传递给测试方法的参数 a, b, 和 expected。
2、使用file_data装饰器
@file_data 允许你从外部文件中读取数据,并以适当的格式传递给测试方法。通常,它支持读取文件中的数据格式如 JSON 或 YAML,并且将数据作为测试的输入。
ddt是可以直接直接json格式的文件读取,但是对于yaml格式的文件,需要额外安装一个第三方的python安装包。
pip install pyyaml -i 镜像源
但是对于其他格式的测试数据文件,需要自己封装一个方法读取文件中的数据。
1️⃣使用JSON 文件作为数据源
首先,创建一个 JSON 文件(例如 data.json),JSON 数据通常使用 字典格式,内容如下:
[{"a": 1, "b": 2, "expected": 3},{"a": -1, "b": 1, "expected": 0},{"a": 100, "b": 200, "expected": 300}
]
接下来,使用 @file_data 装饰器来加载这个 JSON 文件中的数据,并传递给测试方法
import unittest
from ddt import ddt, file_data@ddt
class TestAddition(unittest.TestCase):# 使用 @file_data 从外部 JSON 文件加载数据@file_data('data.json')def test_add(self, a, b, expected):print(f"Testing {a} + {b} = {expected}")self.assertEqual(a + b, expected)if __name__ == "__main__":unittest.main()
@file_data('data.json')
:该装饰器会加载data.json
文件中的数据。- 每一项数据(每个字典)会作为一组输入数据传递给测试方法
test_add
。 - 文件中的每个字典会被解包,作为独立的参数传递给
test_add
。
2️⃣使用 YAML 文件作为数据源
首先,创建一个 YAML 文件(例如 data.yaml
):
- a: 1b: 2expected: 3
- a: -1b: 1expected: 0
- a: 100b: 200expected: 300
接着,你可以使用 @file_data
装饰器来加载该 YAML 文件的数据:
import unittest
from ddt import ddt, file_data@ddt
class TestAddition(unittest.TestCase):# 使用 @file_data 从外部 YAML 文件加载数据@file_data('data.yaml')def test_add(self, a, b, expected):print(f"Testing {a} + {b} = {expected}")self.assertEqual(a + b, expected)if __name__ == "__main__":unittest.main()