pytest fixture及conftest详解二 (pytest fixture用法)

分手后的思念是犯贱 2024-04-01 12:07 174阅读 0赞

在这里插入图片描述


文章目录

  • 一、pytest fixture一些概念
    • 1、pytest fixture 说明
    • 2、pytest fixture几个关键特性
    • 3、pytest fixture定义
  • 二、Pytest fixture用法
    • 用法一:作为参数使用
      • 1.将fixture函数作为参数传递给测试用例
      • 2.同一个用例中传入多个fixture函数
      • 3.fixture函数之间的相互传递
    • 用法二:提供灵活的类似setup和teardown功能
      • 案例
    • 用法三:利用pytest.mark.usefixtures叠加调用多个fixture
    • 用法四:内置fixture函数之pytestconfig详解
      • pytestconfig的源代码
      • pytestconfig fixture函数中有2个比较常用的方法:
        • pytestconfig.getoption 获取命令行参数(对应的参数值):
        • pytestconfig.addini 动态添加pytest.ini配置文件中的参数
      • addini 方法源代码
      • 案例
      • pytest.ini 配置 url地址

一、pytest fixture一些概念

1、pytest fixture 说明

fixture是在测试函数运行前后,由pytest执行的外壳函数。fixture中的代码可以定制,满足多变的测试需求,包括定义传入测试中的数据集、配置测试前系统的初始状态、为批量测试提供数据源等等。fixture是pytest的精髓所在,类似unittest中setup/teardown,但是比它们要强大、灵活很多,它的优势是可以跨文件共享。

2、pytest fixture几个关键特性

a、独立命名,可以通过方法(function)、类(class)、模块(module)、整个项目(session)来激活
b、按照模块化的方式实现,每个fixture都可以相互调用
c、fixture可以实现unittest不能实现的功能,比如unittest中的测试用例和测试用例之间无法传递参数和数据,但是fixture可以解决这个问题
d、fixture的范围从简单的单元拓展到复杂的接口\UI测试,允许根据配置和组件选项对fixture和测试用例进行参数化

3、pytest fixture定义

a、定义fixture和定义普通函数差不多,唯一的区别就是在函数上加个装饰器@pytest.fixture(),fixture命名不要用test_开头,跟用例区分开.
b、fixture装饰器里的scope有4个级别的参数:function、class、module、session;
c、fixture可以有返回值,如果没有return,默认返回None;用例调用fixture的返回值,就是直接把fixture的函数名称作为参数传入
d、fixture可以返回一个元组\列表\字典
e、测试用例可以传单个、多个fixture参数
f、fixture和fixture间可以相互调用

二、Pytest fixture用法

用法一:作为参数使用

1.将fixture函数作为参数传递给测试用例

fixture的名字直接作为测试用例的参数,用例调用fixture的返回值,直接将fixture的函数名称当做变量名称;如果用例需要用到多个fixture的返回数据,fixture也可以返回一个元组,列表或字典,然后从里面取出对应数据。

conftest.py

  1. import pytest
  2. data=[{
  3. 'user':'kobe','pwd':666666},
  4. {
  5. 'user':'curry','pwd':666666},
  6. {
  7. 'user':'james','pwd':666666},]
  8. @pytest.fixture(scope='function',params=data,ids=['data1','data2','data3'],name='fix')
  9. def my_fixture(request):
  10. print('这是前置方法')
  11. yield request.param
  12. print('这是后置方法')

test_demo1.py

  1. import pytest
  2. class TestDemo:
  3. def test_01_work(self,fix):
  4. print('执行测试用例1,',fix)
  5. assert fix['user']=='kobe' and fix['pwd']==666666
  6. def test_02_work(self):
  7. print('执行测试用例2')
  8. class TestDemo1:
  9. def test_03_work(self):
  10. print('执行测试用例3')
  11. def test_04_work(self):
  12. print('执行测试用例4')
  13. if __name__ == '__main__':
  14. pytest.main(['-vs','./test_demo1.py'])

2.同一个用例中传入多个fixture函数

conftest.py

  1. import pytest
  2. @pytest.fixture(scope='function',autouse=True)
  3. def my_fixture():
  4. print('这是前置方法')
  5. yield
  6. print('这是后置方法')
  7. @pytest.fixture(scope='function')
  8. def my_fixture1():
  9. user='kobe'
  10. yield user
  11. @pytest.fixture(scope='function')
  12. def my_fixture2():
  13. pwd=666666
  14. yield pwd

test_run1.py

  1. import pytest
  2. class TestRun:
  3. def test_01_run(self,my_fixture1,my_fixture2):
  4. print('执行测试用例1')
  5. user=my_fixture1
  6. pwd=my_fixture2
  7. print('参数为:', user, pwd)
  8. def test_02_run(self):
  9. print('执行测试用例2',)
  10. if __name__ == '__main__':
  11. pytest.main(['-vs','./test_run1.py'])

执行结果:

  1. test_run1.py::TestRun::test_01_run 这是前置方法
  2. 执行测试用例1
  3. 参数为: kobe 666666
  4. PASSED这是后置方法
  5. test_run1.py::TestRun::test_02_run 这是前置方法
  6. 执行测试用例2
  7. PASSED这是后置方法
  8. ============================== 2 passed in 0.10s ==============================
  9. Process finished with exit code 0

3.fixture函数之间的相互传递

conftest.py

  1. import pytest
  2. @pytest.fixture(scope='function', autouse=True)
  3. def my_fixture():
  4. print('这是前置方法')
  5. yield
  6. print('这是后置方法')
  7. @pytest.fixture(scope='function')
  8. def my_fixture1():
  9. print('获取用户名和密码')
  10. user = 'kobe'
  11. pwd = 666666
  12. yield user, pwd
  13. @pytest.fixture(scope='function')
  14. def my_fixture2(my_fixture1):
  15. print('登录操作,获取token')
  16. user=my_fixture1[0]
  17. pwd=my_fixture1[1]
  18. token = 上面获取大的值
  19. yield token

test_run1.py

  1. import pytest
  2. class TestRun:
  3. def test_01_run(self,my_fixture2):
  4. print('执行测试用例1')
  5. token=my_fixture2
  6. print('参数为:', token)
  7. def test_02_run(self):
  8. print('执行测试用例2',)
  9. if __name__ == '__main__':
  10. pytest.main(['-vs','./test_run1.py'])

执行结果

  1. test_run1.py::TestRun::test_01_run 这是前置方法
  2. 获取用户名和密码
  3. 登录操作,获取token
  4. 执行测试用例1
  5. 参数为: rthfgsdsgfgtynfgsxbvbm
  6. PASSED这是后置方法
  7. test_run1.py::TestRun::test_02_run 这是前置方法
  8. 执行测试用例2
  9. PASSED这是后置方法
  10. ============================== 2 passed in 0.09s ==============================
  11. Process finished with exit code 0

用法二:提供灵活的类似setup和teardown功能

a.Pytest fixture另一个强大的功能就是在函数执行前后增加操作,类似setup和teardown操作,但是比setup和teardown的操作更加灵活;具体使用方式是同样定义一个函数,然后用装饰器标记为fixture,然后在此函数中使用一个yield语句,yield语句之前的就会在测试用例之前使用,yield之后的语句就会在测试用例执行完成之后再执行。

案例

  1. @pytest.fixture()
  2. def fixture_driver():
  3. driver = webdriver.Chrome()
  4. yield driver
  5. driver.quit()
  6. def test_baidu(fixture_driver):
  7. driver = fixture_driver
  8. driver.get("http://www.baidu.com")
  9. el=driver.find_element_by_xpath('//a[text()='新闻']')
  10. el.click()

用法三:利用pytest.mark.usefixtures叠加调用多个fixture

如果一个方法或者一个类中的用例想要同时调用多个fixture,可以使用@pytest.mark.usefixtures()进行叠加;
叠加顺序,先执行的放在底层,后执行的放在上层.
特别注意:
1.与直接传入的fixture不同的是,@pytest.mark.usefixtures无法获取到被fixture装饰的函数的返回值

  1. @pytest.mark.usefixtures的使用场景是:被测试方法需要多个fixture做前后置工作时使用;

conftest.py

  1. import pytest
  2. @pytest.fixture(scope='function', autouse=False)
  3. def my_fixture():
  4. print('这是前置方法1')
  5. yield
  6. print('这是后置方法1')
  7. @pytest.fixture(scope='function',autouse=False)
  8. def my_fixture1():
  9. print('这是前置方法2')
  10. yield
  11. print('这是后置方法2')
  12. @pytest.fixture(scope='function',autouse=False)
  13. def my_fixture2():
  14. print('这是前置方法3')
  15. yield
  16. print('这是后置方法3')

test_run1.py

  1. import pytest
  2. class TestRun:
  3. @pytest.mark.usefixtures('my_fixture2')
  4. @pytest.mark.usefixtures('my_fixture1')
  5. @pytest.mark.usefixtures('my_fixture')
  6. def test_01_run(self,):
  7. print('执行测试用例1' )
  8. def test_02_run(self):
  9. print('执行测试用例2')
  10. if __name__ == '__main__':
  11. pytest.main(['-vs','./test_run1.py'])

执行结果

  1. test_run1.py::TestRun::test_01_run 这是前置方法1
  2. 这是前置方法2
  3. 这是前置方法3
  4. 执行测试用例1
  5. PASSED这是后置方法3
  6. 这是后置方法2
  7. 这是后置方法1
  8. test_run1.py::TestRun::test_02_run 执行测试用例2
  9. PASSED
  10. ============================== 2 passed in 0.09s ==============================

用法四:内置fixture函数之pytestconfig详解

pytestconfig 是pytest框架的一个内置fixture函数,可以获取上下文,它的作用跟 request.config 是一样的,代表pytest配置对象。

pytestconfig的源代码

  1. @fixture(scope="session")
  2. def pytestconfig(request: FixtureRequest) -> Config:
  3. """Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
  4. Example::
  5. def test_foo(pytestconfig):
  6. if pytestconfig.getoption("verbose") > 0:
  7. ...
  8. """
  9. return request.config

pytestconfig fixture函数中有2个比较常用的方法:

pytestconfig.getoption 获取命令行参数(对应的参数值):

不但可以获取pytest框架中自带的命令行参数值,也可以获取用户注册的自定义参数值
在这里插入图片描述

conftest.py

  1. import pytest
  2. @pytest.fixture(scope='session',autouse=True)
  3. def my1_fixture():
  4. print('----这是前置方法----')
  5. yield
  6. print('----这是后置方法----')
  7. # conftest.py
  8. def pytest_addoption(parser):
  9. # parser.addoption():为pytest添加命令行参数
  10. parser.addoption("--cmdopt",
  11. action="store",
  12. default="默认参数值",
  13. help="my option: type1 or type2")
  14. @pytest.fixture()
  15. def cmdopt(pytestconfig):
  16. return pytestconfig.getoption("--cmdopt")

test_run1.py

  1. import pytest
  2. class TestRun:
  3. def test_run_1(self,cmdopt):
  4. # 通过conftest.py文件中定义的fixture函数来获取自定义参数值,
  5. # 但是定义的fixture函数中原理依旧是调用了pytest内置fixture函数pytestconfig
  6. print("test_cmd_1当前获取的参数为:{}".format(cmdopt))
  7. def test_run_2(self,request):
  8. cmdopt = request.config.getoption("cmdopt")
  9. # 直接通过调用pytest内置fixture函数request获取自定义参数值
  10. print("test_cmd_2当前获取的参数为:{}".format(cmdopt))
  11. def test_run3_3(self,pytestconfig):
  12. cmdopt = pytestconfig.getoption("cmdopt")
  13. # 直接通过调用pytest内置fixture函数pytestconfig获取自定义参数值
  14. print("test_cmd_3当前获取的参数为:{}".format(cmdopt))
  15. if __name__ == '__main__':
  16. pytest.main(['-s', '--cmdopt=test'])

命令行执行参数:

  1. D:\project_development\api_pytest>pytest -s --cmdopt=我和我的祖国

执行结果:

  1. collected 3 items
  2. testcases\users\test_run1.py ----这是前置方法----
  3. test_cmd_1当前获取的参数为:我和我的祖国
  4. .test_cmd_2当前获取的参数为:我和我的祖国
  5. .test_cmd_3当前获取的参数为:我和我的祖国
  6. .----这是后置方法----
  7. ======================================================== 3 passed in 0.13s ========================================================
pytestconfig.addini 动态添加pytest.ini配置文件中的参数

1、在添加自定义参数到pytest配置对象中:在conftest.py文件中通过钩子函数 pytest_addoption 中使用 addoption 方法来添加自定义命令行参数。
2、在conftest.py文件中通过钩子函数 pytest_addoption 中使用 addini 方法来添加pytest.ini配置文件中的参数。

addini 方法源代码

name:添加到pytest.ini配置文件中的参数名;
help:对参数名的帮助说明,方便查阅;
type:参数类型,默认None,可以设置:None, “pathlist”, “args”, “linelist”, “bool”;
default:参数默认值;

  1. def addini(self, name, help, type=None, default=None):
  2. """ register an ini-file option.
  3. :name: name of the ini-variable
  4. :type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
  5. or ``bool``.
  6. :default: default value if no ini-file option exists but is queried.
  7. The value of ini-variables can be retrieved via a call to
  8. :py:func:`config.getini(name) <_pytest.config.Config.getini>`.
  9. """
  10. assert type in (None, "pathlist", "args", "linelist", "bool")
  11. self._inidict[name] = (help, type, default)
  12. self._ininames.append(name)

案例

conftest.py

  1. import pytest
  2. @pytest.fixture(scope='session',autouse=True)
  3. def my1_fixture():
  4. print('----这是前置方法----')
  5. yield
  6. print('----这是后置方法----')
  7. def pytest_addoption(parser):
  8. # 添加配置参数
  9. parser.addini(
  10. name="base_url",
  11. type=None,
  12. default="http://www.baidu.com",
  13. help="base_url配置参数"
  14. )
  15. @pytest.fixture()
  16. def base_url(pytestconfig):
  17. return pytestconfig.getini("base_url")

test_run1.py

  1. import pytest
  2. class TestRun:
  3. def test_run_1(self,base_url):
  4. print("当前获取到的base_url = {}".format(base_url))
  5. def test_run_2(self,base_url):
  6. print("当前获取到的base_url = {}".format(base_url))
  7. if __name__ == '__main__':
  8. pytest.main(['-s', '--cmdopt=test'])

命令行执行参数

  1. pytest -s

执行结果

  1. collected 2 items
  2. testcases\users\test_run1.py ----这是前置方法----
  3. 当前获取到的base_url = http://www.baidu.com
  4. .当前获取到的base_url = http://www.baidu.com
  5. .----这是后置方法----
  6. ======================================================== 2 passed in 0.13s ========================================================

pytest.ini 配置 url地址

如果有一天我们的测试环境发生了改变,这时候不需要去改代码,只需在 pytest.ini 配置一个环境地址

特别注意:pytest.ini配置在根目录下

在这里插入图片描述

pytest.ini文件

  1. [pytest]
  2. markers =
  3. smoke: Run the smoke test functions for tasks project
  4. get: Run the test functions that test tasks.get()
  5. url = https://blog.csdn.net/YZL40514131

conftest.py

  1. import pytest
  2. @pytest.fixture(scope='session',autouse=True)
  3. def my1_fixture():
  4. print('----这是前置方法----')
  5. yield
  6. print('----这是后置方法----')
  7. def pytest_addoption(parser):
  8. parser.addoption( "--cmdopt",
  9. action="store",
  10. default="type1",
  11. help="my option: type1 or type2"
  12. )
  13. # 添加参数到pytest.ini
  14. parser.addini('url',
  15. type=None,
  16. default="http://www.baidu.com/",
  17. help='添加 url 访问地址参数')
  18. # 获取 pytest.ini 配置参数
  19. @pytest.fixture(scope="session")
  20. def home_url(pytestconfig):
  21. url = pytestconfig.getini('url')
  22. print("\n读取到配置文件的url地址:%s" % url)
  23. return url

test_work1.py

  1. #encoding=utf-8
  2. import pytest
  3. class TestWork:
  4. def test_h(self,home_url):
  5. print("用例:%s" % home_url)
  6. def test_work1(self):
  7. assert 1 == 1
  8. def test_work2(self):
  9. assert 2 == 5
  10. if __name__ == '__main__':
  11. pytest.main()

执行结果:
在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,174人围观)

还没有评论,来说两句吧...

相关阅读