测试开发技术网站
博客
设计
设计
开发
Python
测试
unittest
运维
Linux基础应用
CI/CD
CI/CD
数据库
数据库
云计算
云计算
云原生
云原生
爬虫
爬虫
数据分析
数据分析
人工智能
人工智能
登录
注册
Pytest----Pytest中fixture基础应用
收藏本文
作者:redrose2100 类别: 日期:2022-05-13 16:04:45 阅读:930 次 消耗积分:0 分
[TOC] ![](https://redrose2100.oss-cn-hangzhou.aliyuncs.com/img/7cd47362-951c-11ee-986a-0242ac110004.png) ## 一、什么是fixture pytest的fixture,就是pytest在执行测试用例函数的时候,先去看测试函数的形参,然后去fixture中找是否有名字匹配的,如果有,则去执行fixture函数的内容,并且根据fixture函数的返回值情况,比如fixture函数有返回值,则会将此返回值传递给测试函数,作为此测试函数中当前形参的实参值传入,声明一个fixture函数只需要在函数定义上面增加@pytest.fixture()声明即可 ## 二、fixture类似函数传值 如下,首先声明了一个fixture函数get_num,这里为了简单的演示其作用,这是打印了一行语句,然后就返回一个数字10,在实际应用中,这里可以做很复杂的处理。当pytest执行测试函数test_func的时候,首先看一下形参,这里比如get_num,然后pytest就去自己的fixture函数列表中找是否有同名的,这里发现有,于是就去执行get_num函数,此外发现get_num这个fixture函数有一个返回值10,于是就将这个返回值10拿过来赋值给当前作为参数形参的get_num的值,这也就是为什么在测试函数中可以直接使用get_num进行断言的原因 test_demo.py代码如下 ```python import pytest @pytest.fixture() def get_num(): print("\nin get_num fixture...") return 10 def test_func(get_num): assert get_num == 9 ``` 执行结果如下: ```bash $ pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 1 item test_demo.py in get_num fixture... F =============================================================================== FAILURES =============================================================================== ______________________________________________________________________________ test_func _______________________________________________________________________________ get_num = 10 def test_func(get_num): > assert get_num == 9 E assert 10 == 9 test_demo.py:9: AssertionError ======================================================================= short test summary info ======================================================================== FAILED test_demo.py::test_func - assert 10 == 9 ========================================================================== 1 failed in 0.10s =========================================================================== ``` ## 三、fixture中继续调用fixture 如下,get_num2的fixture中继续调用了get_num1的fixture,然后测试函数中调用get_num2的fixture,原理都是一样大金额 test_demo.py代码如下: ```python import pytest @pytest.fixture() def get_num1(): print("\nin get_num1 fixture...") return 100 @pytest.fixture() def get_num2(get_num1): print("\nin get_num2 fixture...") return 10+get_num1 def test_func(get_num2): assert get_num2 == 111 ``` 执行结果如下 ```bash $ pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 1 item test_demo.py in get_num1 fixture... in get_num2 fixture... F =============================================================================== FAILURES =============================================================================== ______________________________________________________________________________ test_func _______________________________________________________________________________ get_num2 = 110 def test_func(get_num2): > assert get_num2 == 111 E assert 110 == 111 test_demo.py:14: AssertionError ======================================================================= short test summary info ======================================================================== FAILED test_demo.py::test_func - assert 110 == 111 ========================================================================== 1 failed in 0.10s =========================================================================== ``` ## 四、测试函数同时可以调用多个fixture 如下,test_func测试函数中调用了两个fixture test_demo.py代码如下 ```python import pytest @pytest.fixture() def get_num1(): print("\nin get_num1 fixture...") return 100 @pytest.fixture() def get_num2(): print("\nin get_num2 fixture...") return 200 def test_func(get_num1,get_num2): assert int(get_num1+get_num2) == 301 ``` 执行结果如下 ```bash $ pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 1 item test_demo.py in get_num1 fixture... in get_num2 fixture... F =============================================================================== FAILURES =============================================================================== ______________________________________________________________________________ test_func _______________________________________________________________________________ get_num1 = 100, get_num2 = 200 def test_func(get_num1,get_num2): > assert int(get_num1+get_num2) == 301 E assert 300 == 301 E + where 300 = int((100 + 200)) test_demo.py:14: AssertionError ======================================================================= short test summary info ======================================================================== FAILED test_demo.py::test_func - assert 300 == 301 ========================================================================== 1 failed in 0.10s =========================================================================== ``` ## 五、fixture可以设置自动执行 在定义fixture函数的时候,通过将autouse设置为True即可,如下所示,虽然整个过程中都没有调用init_name这个fixture,但是通过最后执行的结果看,这个init_name的fixture明显是执行了的 test_demo.py如下 ```python import pytest @pytest.fixture() def get_name(): print("\nin get_name fixture...") return [] @pytest.fixture(autouse=True) def init_name(get_name): print("\nin init fixture...") get_name.append("张无忌") get_name.append("张三丰") def test_func(get_name): assert "周芷若" in get_name ``` 执行结果如下 ```bash $ pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 1 item test_demo.py in get_name fixture... in init fixture... F =============================================================================== FAILURES =============================================================================== ______________________________________________________________________________ test_func _______________________________________________________________________________ get_name = ['张无忌', '张三丰'] def test_func(get_name): > assert "周芷若" in get_name E AssertionError: assert '周芷若' in ['张无忌', '张三丰'] test_demo.py:15: AssertionError ======================================================================= short test summary info ======================================================================== FAILED test_demo.py::test_func - AssertionError: assert '周芷若' in ['张无忌', '张三丰'] ========================================================================== 1 failed in 0.11s =========================================================================== ``` ## 六、fixture的范围 在此之前介绍额fixture似乎都是在传递值,像调用函数一样,其实传递值只是fixture的一个很小的应用,fixture最重要的作用是充当类似其他测试框架中的setup和teardown,通过上述的介绍可以得出的一个结论就是,通过调用fixture,可以做到fixture函数的代码是在测试函数之前执行的,试想,如果fixture函数的代码是比如初始化环境的代码,那么此时fixture的作用就完全可以理解为自动化测试用例的setup步骤了,事实上也确实如此,pytest自动化框架确实是fixture中的代码执行认为是setup和teardown的步骤的。 因此当把fixture作为充当setup和teardown的作用,那么问题就来了,setup和teardown是有测试函数级setup和teardown,测试类级setup和teardown,测试模块级setup和teardown,以及整个测试执行最开始和最后的setup和teardown,理解到此,就可以理解fixture的范围了,fixture的范围有以下几个: * function: 测试函数级,这个是fixture默认的范围,如果不设置即为function级别fixture * class: 测试类级的fixture * module: 测试模块级的fixture * package: 测试包级别的fixture * session: 翻译过来叫会话级别的fixture,一个会话实质上就是执行pytest命令的整个过程,即session级实质上就是执行pytest最开始和最后的setup和teardown session,module,package级额fixture一般在conftest.py中定义,其他级别的fixture可以在conftest.py中定义也可以在具体文件中定义,如果是共享给其他文件使用则放在conftest.py中定义,如果是自己文件中独有的,则在自己的文件中定义即可 如下,conftest.py中定义session级的fixture ```python import pytest @pytest.fixture(autouse=True,scope="session") def session_fixture(): print("\nin session_fixture...") ``` test_demo.py中定义function级别的fixture ```python import pytest @pytest.fixture(autouse=True,scope="function") def function_fixture(): print("\nin function_fixture...") def test_func1(): print("in test_func1...") def test_func2(): print("in test_func2...") def test_func3(): print("in test_func3...") ``` 执行结果如下,可以看到session即的只会在最开始执行一次,而function级的则会在每个测试好桉树之前都会执行,这就做到了setup的功能,至于teardown的功能需要使用到yield关键字,后续继续展开 ```bash $ pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 3 items test_demo.py in session_fixture... in function_fixture... in test_func1... . in function_fixture... in test_func2... . in function_fixture... in test_func3... . ========================================================================== 3 passed in 0.02s =========================================================================== ``` ## 七、fixture通过yield关键字实现setup和teardown fixture中,yield关键字之前的代码为setup的步骤,yield之后的代码即为teardown的步骤,yield相当于return,如果有返回值,就直接放在yield,如果没有返回值则直接使用yield即可 如conftest.py中如下: ```python import pytest @pytest.fixture(autouse=True,scope="session") def session_fixture(): print("\nin session_fixture setup ...") yield print("\nin session_fixture teardown ...") ``` test_demo.py中如下: ```python import pytest @pytest.fixture(autouse=True,scope="function") def function_fixture(): print("\nin function_fixture setup ...") yield 10 print("\nin function_fixture teardown ...") def test_func1(): print("in test_func1...") def test_func2(): print("in test_func2...") def test_func3(function_fixture): print("in test_func3...") assert function_fixture == 11 ``` 执行结果如下,此时已经实现了setup和teardown的功能,此外function级的fixture还返回了一个数值 ```bash $ pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 3 items test_demo.py in session_fixture setup ... in function_fixture setup ... in test_func1... . in function_fixture teardown ... in function_fixture setup ... in test_func2... . in function_fixture teardown ... in function_fixture setup ... in test_func3... F in function_fixture teardown ... in session_fixture teardown ... =============================================================================== FAILURES =============================================================================== ______________________________________________________________________________ test_func3 ______________________________________________________________________________ function_fixture = 10 def test_func3(function_fixture): print("in test_func3...") > assert function_fixture == 11 E assert 10 == 11 test_demo.py:17: AssertionError ======================================================================= short test summary info ======================================================================== FAILED test_demo.py::test_func3 - assert 10 == 11 ===================================================================== 1 failed, 2 passed in 0.10s ====================================================================== ``` ## 八、yield的缺陷及解决方案 首先看下如下例子,test_demo.py内容如下: ```python import pytest @pytest.fixture(autouse=True,scope="function") def function_fixture(): print("\nin function_fixture setup ...") a=1/0 yield 10 print("\nin function_fixture teardown ...") def test_func3(function_fixture): print("in test_func3...") assert function_fixture == 11 ``` 执行结果如下,仔细观察就会发现,当在setup部分出错了,则teardown部分根本就不会执行了,这一点对于自动化测试来讲是有一定弊端的,比如在setup部分配置了一些基础数据,然后由于某种原因出错了,这样由于根本不会执行teardown的操作,从而导致整体环境就有残留了,这就是yield关键字存在的弊端 ```bash $ pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 1 item test_demo.py in function_fixture setup ... E ================================================================================ ERRORS ================================================================================ _____________________________________________________________________ ERROR at setup of test_func3 _____________________________________________________________________ @pytest.fixture(autouse=True,scope="function") def function_fixture(): print("\nin function_fixture setup ...") > a=1/0 E ZeroDivisionError: division by zero test_demo.py:6: ZeroDivisionError ======================================================================= short test summary info ======================================================================== ERROR test_demo.py::test_func3 - ZeroDivisionError: division by zero =========================================================================== 1 error in 0.10s =========================================================================== ``` 如下,通过request的addfinalizer则可以解决这个问题 test_demo.py内容如下: ```python import pytest @pytest.fixture(autouse=True,scope="function") def function_fixture(request): def teardown_op(): print("\nin function_fixture teardown ...") request.addfinalizer(teardown_op) print("\nin function_fixture setup ...") a=10/0 return 10 def test_func3(function_fixture): print("in test_func3...") assert function_fixture == 11 ``` 执行结果如下,可以发现虽然setup部分虽然出错了,但是teardown仍然可以正常执行 ```bash pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 1 item test_demo.py in function_fixture setup ... E in function_fixture teardown ... ================================================================================ ERRORS ================================================================================ _____________________________________________________________________ ERROR at setup of test_func3 _____________________________________________________________________ request =
> @pytest.fixture(autouse=True,scope="function") def function_fixture(request): def teardown_op(): print("\nin function_fixture teardown ...") request.addfinalizer(teardown_op) print("\nin function_fixture setup ...") > a=10/0 E ZeroDivisionError: division by zero test_demo.py:10: ZeroDivisionError ======================================================================= short test summary info ======================================================================== ERROR test_demo.py::test_func3 - ZeroDivisionError: division by zero =========================================================================== 1 error in 0.10s =========================================================================== ``` 上面的优化能解决teardown不执行的问题,但是还有另外的问题,比如setup有5个步骤,teardown有对应的5个步骤,则按照上面的解决办法带来一个问题是比如setup中在第二个步骤中报错了,则此时相当于setup只成功执行了一个步骤,而上述解决方案中的teardown中不得不把5个步骤全部执行,这在一些场景中也是有问题的 为了解决上面的问题,下面的解决方案则可能稍微更好一些,即还是使用yield关键字,不过每一个fixture的setup只做原子操作,teardown也只做与setup对应的原子操作,如下代码中f1,f2,f3中的setup和teardown分别只做原子操作,假设在f2的setup中由于某种原因出错,此时f2因为setup报错了,因此f2没有必要执行teardown,而采用这种方案即使f2中setup失败了,f1的teardown仍然执行,如此则达到了只要setup成功了就会执行其对应的teardown操作 如下test_demo.py代码如下: ```python import pytest @pytest.fixture(scope="function") def f1(): print("\nin f1 fixture setup...") yield print("\nin f1 fixture teardown...") @pytest.fixture(scope="function") def f2(f1): print("\nin f2 fixture setup...") a=1/0 yield print("\nin f2 fixture teardown...") @pytest.fixture(scope="function") def f3(f2): print("\nin f3 fixture setup...") yield 10 print("\nin f3 fixture teardown...") def test_func3(f3): print("\nin test_func3...") assert f3 == 11 ``` 执行结果如下: ```bash $ pytest -s ========================================================================= test session starts ========================================================================== platform win32 -- Python 3.9.7, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 rootdir: D:\src\blog\tests, configfile: pytest.ini plugins: allure-pytest-2.9.43, caterpillar-pytest-0.0.2, hypothesis-6.31.6, forked-1.3.0, rerunfailures-10.1, xdist-2.3.0 collected 1 item test_demo.py in f1 fixture setup... in f2 fixture setup... E in f1 fixture teardown... ================================================================================ ERRORS ================================================================================ _____________________________________________________________________ ERROR at setup of test_func3 _____________________________________________________________________ f1 = None @pytest.fixture(scope="function") def f2(f1): print("\nin f2 fixture setup...") > a=1/0 E ZeroDivisionError: division by zero test_demo.py:12: ZeroDivisionError ======================================================================= short test summary info ======================================================================== ERROR test_demo.py::test_func3 - ZeroDivisionError: division by zero =========================================================================== 1 error in 0.10s =========================================================================== ```
始终坚持开源开放共享精神,同时感谢您的充电鼓励和支持!
版权所有,转载本站文章请注明出处:redrose2100, http://blog.redrose2100.com/article/181
上一篇:
Pytest执行脚本
下一篇:
Pytest----fixture高级应用
搜索
个人成就
出版书籍
《Pytest企业级应用实战》
测试开发技术全栈公众号
测试开发技术全栈公众号
DevOps技术交流微信群
加微信邀请进群
常用网站链接
开源软件洞察
云原生技术栈全景图
Python语言官方文档
Golang官方文档
Docker官方文档
Jenkins中文用户手册
Scrapy官方文档
VUE官方文档
Harbor官方文档
openQA官方文档
云原生开源社区
开源中国
Kubernetes中文文档
Markdown语法官方教程
Kubernetes中文社区
Kubersphere官方文档
BootStrap中文网站
JavaScript中文网
NumPy官方文档
Pandas官方文档
GitLink确实开源网站
数据库排名网站
编程语言排名网站
SEO综合查询网站
数学加减法练习自动生成网站
Kickstart Generator
文章分类
最新文章
最多阅读
特别推荐
×
Close
登录
注册
找回密码
登录邮箱:
登录密码:
图片验证码:
注册邮箱:
注册密码:
邮箱验证码:
发送邮件
注册邮箱:
新的密码:
邮箱验证码:
发送邮件