文章目录
一、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
import pytest
data=[{'user':'kobe','pwd':666666},
{'user':'curry','pwd':666666},
{'user':'james','pwd':666666},]
@pytest.fixture(scope='function',params=data,ids=['data1','data2','data3'],name='fix')
def my_fixture(request):
print('这是前置方法')
yield request.param
print('这是后置方法')
test_demo1.py
import pytest
class TestDemo:
def test_01_work(self,fix):
print('执行测试用例1,',fix)
assert fix['user']=='kobe' and fix['pwd']==666666
def test_02_work(self):
print('执行测试用例2')
class TestDemo1:
def test_03_work(self):
print('执行测试用例3')
def test_04_work(self):
print('执行测试用例4')
if __name__ == '__main__':
pytest.main(['-vs','./test_demo1.py'])
2.同一个用例中传入多个fixture函数
conftest.py
import pytest
@pytest.fixture(scope='function',autouse=True)
def my_fixture():
print('这是前置方法')
yield
print('这是后置方法')
@pytest.fixture(scope='function')
def my_fixture1():
user='kobe'
yield user
@pytest.fixture(scope='function')
def my_fixture2():
pwd=666666
yield pwd
test_run1.py
import pytest
class TestRun:
def test_01_run(self,my_fixture1,my_fixture2):
print('执行测试用例1')
user=my_fixture1
pwd=my_fixture2
print('参数为:', user, pwd)
def test_02_run(self):
print('执行测试用例2',)
if __name__ == '__main__':
pytest.main(['-vs','./test_run1.py'])
执行结果:
test_run1.py::TestRun::test_01_run 这是前置方法
执行测试用例1
参数为: kobe 666666
PASSED这是后置方法
test_run1.py::TestRun::test_02_run 这是前置方法
执行测试用例2
PASSED这是后置方法
============================== 2 passed in 0.10s ==============================
Process finished with exit code 0
3.fixture函数之间的相互传递
conftest.py
import pytest
@pytest.fixture(scope='function', autouse=True)
def my_fixture():
print('这是前置方法')
yield
print('这是后置方法')
@pytest.fixture(scope='function')
def my_fixture1():
print('获取用户名和密码')
user = 'kobe'
pwd = 666666
yield user, pwd
@pytest.fixture(scope='function')
def my_fixture2(my_fixture1):
print('登录操作,获取token')
user=my_fixture1[0]
pwd=my_fixture1[1]
token = 上面获取大的值
yield token
test_run1.py
import pytest
class TestRun:
def test_01_run(self,my_fixture2):
print('执行测试用例1')
token=my_fixture2
print('参数为:', token)
def test_02_run(self):
print('执行测试用例2',)
if __name__ == '__main__':
pytest.main(['-vs','./test_run1.py'])
执行结果
test_run1.py::TestRun::test_01_run 这是前置方法
获取用户名和密码
登录操作,获取token
执行测试用例1
参数为: rthfgsdsgfgtynfgsxbvbm
PASSED这是后置方法
test_run1.py::TestRun::test_02_run 这是前置方法
执行测试用例2
PASSED这是后置方法
============================== 2 passed in 0.09s ==============================
Process finished with exit code 0
用法二:提供灵活的类似setup和teardown功能
a.Pytest fixture另一个强大的功能就是在函数执行前后增加操作,类似setup和teardown操作,但是比setup和teardown的操作更加灵活;具体使用方式是同样定义一个函数,然后用装饰器标记为fixture,然后在此函数中使用一个yield语句,yield语句之前的就会在测试用例之前使用,yield之后的语句就会在测试用例执行完成之后再执行。
案例
@pytest.fixture()
def fixture_driver():
driver = webdriver.Chrome()
yield driver
driver.quit()
def test_baidu(fixture_driver):
driver = fixture_driver
driver.get("http://www.baidu.com")
el=driver.find_element_by_xpath('//a[text()='新闻']')
el.click()
用法三:利用pytest.mark.usefixtures叠加调用多个fixture
如果一个方法或者一个类中的用例想要同时调用多个fixture,可以使用@pytest.mark.usefixtures()进行叠加;
叠加顺序,先执行的放在底层,后执行的放在上层.
特别注意:
1.与直接传入的fixture不同的是,@pytest.mark.usefixtures无法获取到被fixture装饰的函数的返回值
2. @pytest.mark.usefixtures的使用场景是:被测试方法需要多个fixture做前后置工作时使用;
conftest.py
import pytest
@pytest.fixture(scope='function', autouse=False)
def my_fixture():
print('这是前置方法1')
yield
print('这是后置方法1')
@pytest.fixture(scope='function',autouse=False)
def my_fixture1():
print('这是前置方法2')
yield
print('这是后置方法2')
@pytest.fixture(scope='function',autouse=False)
def my_fixture2():
print('这是前置方法3')
yield
print('这是后置方法3')
test_run1.py
import pytest
class TestRun:
@pytest.mark.usefixtures('my_fixture2')
@pytest.mark.usefixtures('my_fixture1')
@pytest.mark.usefixtures('my_fixture')
def test_01_run(self,):
print('执行测试用例1' )
def test_02_run(self):
print('执行测试用例2')
if __name__ == '__main__':
pytest.main(['-vs','./test_run1.py'])
执行结果
test_run1.py::TestRun::test_01_run 这是前置方法1
这是前置方法2
这是前置方法3
执行测试用例1
PASSED这是后置方法3
这是后置方法2
这是后置方法1
test_run1.py::TestRun::test_02_run 执行测试用例2
PASSED
============================== 2 passed in 0.09s ==============================
用法四:内置fixture函数之pytestconfig详解
pytestconfig 是pytest框架的一个内置fixture函数,可以获取上下文,它的作用跟 request.config 是一样的,代表pytest配置对象。
pytestconfig的源代码
@fixture(scope="session")
def pytestconfig(request: FixtureRequest) -> Config:
"""Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
Example::
def test_foo(pytestconfig):
if pytestconfig.getoption("verbose") > 0:
...
"""
return request.config
pytestconfig fixture函数中有2个比较常用的方法:
pytestconfig.getoption 获取命令行参数(对应的参数值):
不但可以获取pytest框架中自带的命令行参数值,也可以获取用户注册的自定义参数值
conftest.py
import pytest
@pytest.fixture(scope='session',autouse=True)
def my1_fixture():
print('----这是前置方法----')
yield
print('----这是后置方法----')
# conftest.py
def pytest_addoption(parser):
# parser.addoption():为pytest添加命令行参数
parser.addoption("--cmdopt",
action="store",
default="默认参数值",
help="my option: type1 or type2")
@pytest.fixture()
def cmdopt(pytestconfig):
return pytestconfig.getoption("--cmdopt")
test_run1.py
import pytest
class TestRun:
def test_run_1(self,cmdopt):
# 通过conftest.py文件中定义的fixture函数来获取自定义参数值,
# 但是定义的fixture函数中原理依旧是调用了pytest内置fixture函数pytestconfig
print("test_cmd_1当前获取的参数为:{}".format(cmdopt))
def test_run_2(self,request):
cmdopt = request.config.getoption("cmdopt")
# 直接通过调用pytest内置fixture函数request获取自定义参数值
print("test_cmd_2当前获取的参数为:{}".format(cmdopt))
def test_run3_3(self,pytestconfig):
cmdopt = pytestconfig.getoption("cmdopt")
# 直接通过调用pytest内置fixture函数pytestconfig获取自定义参数值
print("test_cmd_3当前获取的参数为:{}".format(cmdopt))
if __name__ == '__main__':
pytest.main(['-s', '--cmdopt=test'])
命令行执行参数:
D:\project_development\api_pytest>pytest -s --cmdopt=我和我的祖国
执行结果:
collected 3 items
testcases\users\test_run1.py ----这是前置方法----
test_cmd_1当前获取的参数为:我和我的祖国
.test_cmd_2当前获取的参数为:我和我的祖国
.test_cmd_3当前获取的参数为:我和我的祖国
.----这是后置方法----
======================================================== 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:参数默认值;
def addini(self, name, help, type=None, default=None):
""" register an ini-file option.
:name: name of the ini-variable
:type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
or ``bool``.
:default: default value if no ini-file option exists but is queried.
The value of ini-variables can be retrieved via a call to
:py:func:`config.getini(name) <_pytest.config.Config.getini>`.
"""
assert type in (None, "pathlist", "args", "linelist", "bool")
self._inidict[name] = (help, type, default)
self._ininames.append(name)
案例
conftest.py
import pytest
@pytest.fixture(scope='session',autouse=True)
def my1_fixture():
print('----这是前置方法----')
yield
print('----这是后置方法----')
def pytest_addoption(parser):
# 添加配置参数
parser.addini(
name="base_url",
type=None,
default="http://www.baidu.com",
help="base_url配置参数"
)
@pytest.fixture()
def base_url(pytestconfig):
return pytestconfig.getini("base_url")
test_run1.py
import pytest
class TestRun:
def test_run_1(self,base_url):
print("当前获取到的base_url = {}".format(base_url))
def test_run_2(self,base_url):
print("当前获取到的base_url = {}".format(base_url))
if __name__ == '__main__':
pytest.main(['-s', '--cmdopt=test'])
命令行执行参数
pytest -s
执行结果
collected 2 items
testcases\users\test_run1.py ----这是前置方法----
当前获取到的base_url = http://www.baidu.com
.当前获取到的base_url = http://www.baidu.com
.----这是后置方法----
======================================================== 2 passed in 0.13s ========================================================
pytest.ini 配置 url地址
如果有一天我们的测试环境发生了改变,这时候不需要去改代码,只需在 pytest.ini 配置一个环境地址
特别注意:pytest.ini配置在根目录下
pytest.ini文件
[pytest]
markers =
smoke: Run the smoke test functions for tasks project
get: Run the test functions that test tasks.get()
url = https://blog.csdn.net/YZL40514131
conftest.py
import pytest
@pytest.fixture(scope='session',autouse=True)
def my1_fixture():
print('----这是前置方法----')
yield
print('----这是后置方法----')
def pytest_addoption(parser):
parser.addoption( "--cmdopt",
action="store",
default="type1",
help="my option: type1 or type2"
)
# 添加参数到pytest.ini
parser.addini('url',
type=None,
default="http://www.baidu.com/",
help='添加 url 访问地址参数')
# 获取 pytest.ini 配置参数
@pytest.fixture(scope="session")
def home_url(pytestconfig):
url = pytestconfig.getini('url')
print("\n读取到配置文件的url地址:%s" % url)
return url
test_work1.py
#encoding=utf-8
import pytest
class TestWork:
def test_h(self,home_url):
print("用例:%s" % home_url)
def test_work1(self):
assert 1 == 1
def test_work2(self):
assert 2 == 5
if __name__ == '__main__':
pytest.main()
执行结果: