- 这一篇文章专门给大家讲解pytest中关于用例执行的前后置步骤处理,pytest中用例执行的前后置处理既可以通过测试夹具(fixtrue)来实现,也可以通过xunit 风格的前后置方法来实现。接下来我们一起看看如何具体使用。
一、xunit风格的前后置方法
1、函数用例的前后置方法
- 在模块中以函数形式定义用例,可以通过
setup_function
和teardown_function
来定义函数用例的前后置方法,使用案例如下:
def setup_function(function): print("函数用例前置方法执行") def teardown_function(function): print("函数用例后置方法执行") def test_01(): print('----用例方法01------')
- 运行结果:
C:\testcases>pytest -s ========================= test session starts ========================= platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0 cachedir: .pytest_cache rootdir: C:\testcases plugins: testreport-1.1.2 collected 1 item test_demo.py 函数用例前置方法执行 ----用例方法01------ . 函数用例后置方法执行 ========================= 1 passed in 0.27s =========================
2、测试类中用例的前后置方法
2.1、类级别的前后置方法
- pytest中测试类级别的前后置方法
setup_class和teardown_class
,分别在测试类中的用例执行之前执行,和测试类中所有用例执行完毕之后执行,具体使用如下:
class TestDome: def test_01(self): print('----测试用例:test_01------') def test_02(self): print('----测试用例:test_02------') @classmethod def setup_class(cls): print("测试类前置方法---setup_class---") @classmethod def teardown_class(cls): print("测试类后置方法---teardown_class---")
2.2、用例级别的前后置方法
- pytest中测试类中用例级别的的前后置方法
setup_method和teardown_method
,分别在测试类中的用例执行之前执行,和测试类中所有用例执行完毕之后执行,具体使用如下:
class TestDome: def test_01(self): print('----测试用例:test_01------') def test_02(self): print('----测试用例:test_02------') @classmethod def setup_class(cls): print("测试类前置方法---setup_class---") @classmethod def teardown_class(cls): print("测试类后置方法---teardown_class---") def setup_method(function): print("测试用例前置方法---setup_method---") def teardown_method(function): print("测试用例后置方法---teardown_method---")
- 运行结果:
C:\testcases>pytest -s ==================== test session starts ==================== platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0 rootdir: C:\testcases plugins: testreport-1.1.2 collected 2 items test_demo.py 测试类前置方法---setup_class--- 测试用例前置方法---setup_method--- ----测试用例:test_01------. 测试用例后置方法---teardown_method--- 测试用例前置方法---setup_method--- ----测试用例:test_02------. 测试用例后置方法---teardown_method--- 测试类后置方法---teardown_class--- ==================== 2 passed in 0.30s =======================
3、模块级别的前后置方法
- pytest中还有setup_module和teardown_module这两个用来设置模块级别前后置方法的函数,定义在模块中,会在整个模块中所有的用例执行前和用例全部执行完毕之后会执行,具体使用如下
class TestDome: def test_01(self): print('----测试用例:test_01------') class TestDome2: def test_02(self): print('----测试用例:test_02------') def setup_module(module): print("测试模块的前置方法---setup_module---") def teardown_module(module): print("测试模块的前置方法---teardown_module---")
- 运行结果:
C:\testcases>pytest -s ====================== test session starts ====================== platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0 rootdir: C:\testcases plugins:testreport-1.1.2 collected 2 items test_demo.py 测试模块的前置方法---setup_module--- ----测试用例:test_01------ .----测试用例:test_02------ .测试模块的前置方法---teardown_module--- ====================== 2 passed in 0.27s ======================
二、fixture机制
- 前面我们介绍了pytest中xunit风格的前后置方法,接下来我们来看一下pytest中功能更加强大的fixture机制(测试夹具)的使用。
1、测试夹具的级别和定义
- 测试夹具需要使用pytest.fixture这个装饰器来定义,pytest中的测试夹具有如下几个级别:用例级别、测试类级别、模块级别,包级别,会话级别。接下来我们一起来看看夹具定义语法。
- 夹具定义可以通过参数scope指定夹具的级别,如果不指定夹具级别,scope 默认值为function(用例级别)
- 用例级别:scope = function
- 测试类级:scope = class
- 模块级别:scope = module
- 包级别: scope = package
- 会话级别:scope = session
- 用法:
@pytest.fixture(scope='指定夹具的级别') def work(): # 前置执行脚本 yield # 后置执行脚本
- 测试夹具本质上是一个生成器函数,生产器函数在使用next进行迭代时,执行到yeild会返回数据,暂停执行,等待下一次进行迭代时才会继续执行,pytest夹具就是利用的生成器的机制,通过yeild在测试夹具将前后置代码分开执行。
- 注意点: 夹具只有在定义夹具的范围内才能使用。如果夹具是在类中定义的,则只能由该类内的测试用例使用。但是如果在模块的全局范围内定义的夹具,那么该模块中的每个测试用例,即使它是在一个类中定义的,都可以使用它。
- 知道了怎么定义夹具,那么接下来我们来看看如何使用夹具。
2、夹具的使用
- 测试夹具定义好之后,测试函数通过将它们声明为参数,来指定执行用例之前要执行的夹具。
- 当 pytest 开始运行测试时,它会查看该测试函数定义的形参,然后搜索与这些参数同名的测试夹具。一旦 pytest 找到它们,它就会运行这些夹具,接收它们返回的内容(如果有的话),并将这些返回内容作为参数传递给测试函数。
- 注意点:当我们使用夹具时,如果夹具的前置脚本执行完,有数据要传递用例,需要传递的数据写在yield后面即可,在使用夹具的用例或者方法中,可以通过定义的形参来获取yeild返回的数据
2.1、在用例中使用夹具
- 不管是函数形式定义的测试用例,还是测试类中方法的形式定义的用例,在使用的时候都是一样的,直接定义一个和要使用的夹具同名的形参即可。
import pytest # 定义一个用例级别的夹具 @pytest.fixture def my_fixture(): print('------my_fixture---用例前置执行脚本--------') yield print('------my_fixture---用例后置执行脚本--------') # 函数用例 指定测试夹具 def test_func__01(my_fixture): print("测试用例----test_func__01----") class TestDome: # 函数用例 指定测试夹具 def test_02(self, my_fixture): print('----测试用例:test_02------') # 函数用例 指定测试夹具 def test_03(self): print('----测试用例:test_03------')
- 运行结果:
C:\testcases>pytest -s ======================== test session starts ======================== platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0 rootdir: C:\testcases plugins: testreport-1.1.2 collected 2 items test_demo.py ------my_fixture---前置执行脚本-------- 测试用例----test_func__01----. ------my_fixture---后置执行脚本-------- ------my_fixture---前置执行脚本-------- ----测试用例:test_02------. ------my_fixture---后置执行脚本-------- ----测试用例:test_03------ ======================== 2 passed in 0.27s ========================
- 上面test_func__01和test_02这两个用例在定义时指定了测试夹具,而test_03则没有指定执行的夹具,执行用例时库看到指定了夹具的用例,执行了对应的夹具。
2.2、测试类和模块指定夹具
- 上面我们通过给用例方法加形参来给单个测试用例指定测试夹具。如果一个测试类中有很多测试用例或者一个模块中有很多用例,都要指定同一个测试夹具,要我们则可以通过需求1:给测试类中所有的用例指定夹具usefixtures给测试类或测试模块指定测试夹具。
- 需求1:给测试类中所有的用例指定夹具
import pytest @pytest.fixture def my_fixture(): print('------my_fixture---用例前置执行脚本--------') yield print('------my_fixture---用例后置执行脚本--------') # TestDome这个测试类的所有测试用例均执行my_fixture这个夹具 @pytest.mark.usefixtures('my_fixture') class TestDome: # 函数用例 指定测试夹具 def test_02(self): print('----测试用例:test_01------') # 函数用例 指定测试夹具 def test_03(self): print('----测试用例:test_02------')
- 运行结果:
D:\Pycharm_workspace>pytest -v -s test_login.py ================================================= test session starts ================================================= platform win32 -- Python 3.10.10, pytest-7.3.1, pluggy-1.0.0 -- D:\software\Python310\python.exe cachedir: .pytest_cache metadata: {'Python': '3.10.10', 'Platform': 'Windows-10-10.0.19045-SP0', 'Packages': {'pytest': '7.3.1', 'pluggy': '1.0.0'}, 'Plugins': {'allure-pytest': '2.13.2', 'Faker': '18.10.1', 'base-url': '2.0.0', 'html': '3.2.0', 'metadata': '3.0.0', 'playwright': '0.3.3', 'testreport': '1.1.6'}, 'JAVA_HOME': 'D:\\software\\Program Files\\Java\\jdk-11.0.12'} rootdir: D:\Pycharm_workspace\py63\d3_0719_python数据类型_列表与元组 plugins: allure-pytest-2.13.2, Faker-18.10.1, base-url-2.0.0, html-3.2.0, metadata-3.0.0, playwright-0.3.3, testreport-1.1.6 collected 2 items test_login.py::TestDome::test_02 ------my_fixture---用例前置执行脚本-------- ----测试用例:test_01------ PASSED------my_fixture---用例后置执行脚本-------- test_login.py::TestDome::test_03 ------my_fixture---用例前置执行脚本-------- ----测试用例:test_02------ PASSED------my_fixture---用例后置执行脚本-------- ================================================== 2 passed in 0.10s ==================================================
- 上面TestDome类中test_02和test_03使用了测试夹具
- 需求2:使用pytestmark 在测试模块级别 指定模块所有用例执行的夹具
import pytest @pytest.fixture def my_fixture(): print('------my_fixture---用例前置执行脚本--------') yield print('------my_fixture---用例后置执行脚本--------') # 当前模块中所有的用例,均执行my_fixture这个测试夹具 pytestmark = pytest.mark.usefixtures('my_fixture') # 函数用例 指定测试夹具 def test_func__01(my_fixture): print("测试用例————test_func__01——————") # TestDome这个测试类的所有测试用例均执行my_fixture这个夹具 @pytest.mark.usefixtures('my_fixture') class TestDome: # 函数用例 指定测试夹具 def test_02(self): print('----测试用例:test_01------') # 函数用例 指定测试夹具 def test_03(self): print('----测试用例:test_02------')
- 运行结果:
D:\Pycharm_workspace>pytest -v -s test_login.py ================================================= test session starts ================================================= platform win32 -- Python 3.10.10, pytest-7.3.1, pluggy-1.0.0 -- D:\software\Python310\python.exe cachedir: .pytest_cache metadata: {'Python': '3.10.10', 'Platform': 'Windows-10-10.0.19045-SP0', 'Packages': {'pytest': '7.3.1', 'pluggy': '1.0.0'}, 'Plugins': {'allure-pytest': '2.13.2', 'Faker': '18.10.1', 'base-url': '2.0.0', 'html': '3.2.0', 'metadata': '3.0.0', 'playwright': '0.3.3', 'testreport': '1.1.6'}, 'JAVA_HOME': 'D:\\software\\Program Files\\Java\\jdk-11.0.12'} rootdir: D:\Pycharm_workspace\py63\d3_0719_python数据类型_列表与元组 plugins: allure-pytest-2.13.2, Faker-18.10.1, base-url-2.0.0, html-3.2.0, metadata-3.0.0, playwright-0.3.3, testreport-1.1.6 collected 3 items test_login.py::test_func__01 ------my_fixture---用例前置执行脚本-------- 测试用例————test_func__01—————— PASSED------my_fixture---用例后置执行脚本-------- test_login.py::TestDome::test_02 ------my_fixture---用例前置执行脚本-------- ----测试用例:test_01------ PASSED------my_fixture---用例后置执行脚本-------- test_login.py::TestDome::test_03 ------my_fixture---用例前置执行脚本-------- ----测试用例:test_02------ PASSED------my_fixture---用例后置执行脚本-------- ================================================== 3 passed in 0.10s ==================================================
- test_login.py模块中所有用例都使用了测试夹具
2.3、在夹具中引用夹具
- pytest 的最大优势之一是其极其灵活的夹具系统。通过测试夹具我们可以将极为复杂化的前后置依赖,拆分为更简单单一功能的测试夹具,通过在夹具中引用其他的夹具,来组织不同用例所需的复杂依赖环境。接下来我们通过一个案例来给大家演示如何使用。
import pytest # 用户注册的夹具 @pytest.fixture def register_user(): print('---用户注册的夹具前置执行----') # ...注册代码省略,注册的用户信息如下 user_info = {'user': 'lemonban', 'pwd': '123456'} yield user_info print('---用户注册的夹具后置执行----') # 用户登录的夹具,通过定义形参来使用register_user这个夹具 @pytest.fixture def user_login(register_user): print('---用户登录的夹具前置执行----') # 获取register_user结局前置脚本执行完,yeild传递出来的数据 user_info = register_user # ...登录代码省略,下面为登录得到的token token = 'sdjasjdask' yield token print('---用户登录的夹具后置执行----') # 函数用例 指定使用测试夹具user_login def test_func__01(user_login): token = user_login print("测试用例夹具user_login传递过来的token:",token) print("测试用例---test_func__01---")
- 运行结果:
C:\testcases>pytest -s ======================== test session starts ======================== platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0 rootdir: C:\testcases plugins: testreport-1.1.2 collected 1 item test_demo.py ---用户注册的夹具前置执行---- 夹具register_user传递过来的用户信息: {'user': 'lemonban', 'pwd': '123456'} ---用户登录的夹具前置执行---- 测试用例夹具user_login传递过来的token: sdjasjdask 测试用例---test_func__01---. ---用户登录的夹具后置执行---- ---用户注册的夹具后置执行----
2.4、自动使用夹具
- 在定义测试夹具 我们可以给夹具的装饰器加参数autouse=True来使夹具成为自动执行的夹具。具体案例如下:
import pytest @pytest.fixture(autouse=True) def my_fixture(): print('------my_fixture---前置执行脚本--------') yield print('------my_fixture---后置执行脚本--------') class TestDome: # 函数用例 指定测试夹具 def test_02(self): print('----测试用例:test_01------') # 函数用例 指定测试夹具 def test_03(self): print('----测试用例:test_02------') class TestDome2: # 函数用例 指定测试夹具 def test_03(self): print('----测试用例:test_03------')
- 运行结果:
C:\testcases>pytest -s ======================== test session starts ======================== platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0 rootdir: C:\testcases plugins: testreport-1.1.2 collected 3 items test_demo.py ------my_fixture---前置执行脚本-------- ----测试用例:test_01------. ------my_fixture---后置执行脚本-------- ------my_fixture---前置执行脚本-------- ----测试用例:test_02------. ------my_fixture---后置执行脚本-------- ------my_fixture---前置执行脚本-------- ----测试用例:test_03------. ------my_fixture---后置执行脚本-------- ======================== 3 passed in 0.29s ========================
- 从上面的执行结果我们可以看到,每条用例执行之前都自动执行了测试夹具my_fixture
3、conftest.py
- 在一个项目的测试中,大多数情况下会有多个类、模块、或者包要使用相同的测试夹具。这种情况下如果我们把测试夹具定义在某一个模块中则无法实现共享,针对这种情况,我们可以把需要共享的测试夹具放入一个单独的
conftest.py
文件中 ,这样多个可以实现多个测试模块共享了 - 注意点 : pytest运行测试时,如果项目中有
conftest.py
,那么pytest会自动加载conftest.py模块中的内容,可以把conftest看出pytest会自动加载的插件模块,后续的教程中会涉及到在conftest.py
中定义pytest的hooks函数 - 接下来我们来看一个conftest.py定义测试夹具的案例
- conftest.py中写夹具
# conftest.py import pytest @pytest.fixture def my_fixture(): print('------my_fixture---前置执行脚本--------') yield print('------my_fixture---后置执行脚本--------')
2. 在test_demo1.py的用例用使用conftest.py中定义的夹具
# test_demo1.py class TestDome: # 函数用例 指定测试夹具 def test_02(self,my_fixture): print('----测试用例:test_01------') # 函数用例 指定测试夹具 def test_03(self,my_fixture): print('----测试用例:test_02------')
3. 在test_demo2.py的用例用使用conftest.py中定义的夹具
# test_demo2.py class TestDome2: # 函数用例 指定测试夹具 def test_03(self,my_fixture): print('----测试用例:test_03------')
- 执行结果:
C:\testcases>pytest -s ======================== test session starts ======================== platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0 rootdir: C:\testcases plugins: testreport-1.1.2 collected 3 items test_demo.py ------my_fixture---前置执行脚本-------- ----测试用例:test_01------. ------my_fixture---后置执行脚本-------- ------my_fixture---前置执行脚本-------- ----测试用例:test_02------. ------my_fixture---后置执行脚本-------- test_demo2.py ------my_fixture---前置执行脚本-------- ----测试用例:test_03------. ------my_fixture---后置执行脚本-------- ======================== 3 passed in 0.29s ========================
- 上述案例中我们可以发现 test_demo.py和test_demo2.py中的用例可以成功使用conftest.py中的测试用例。