一、fixture简介
学pytest就不得不说fixture,fixture是pytest的精髓所在,类似unittest中setup/teardown这种前后置东西。但是比它们要强大、灵活很多,它的优势是可以跨文件共享
fixture的目的是提供一个固定基线,在该基线上测试可以可靠地和重复地执行。fixture提供了区别于传统单元测试(setup/teardown)有显著改进
1、有独立的命名,并通过声明它们从测试函数、模块、类或整个项目中的使用来激活。
2、按模块化的方式实现,每个fixture都可以互相调用。
3、fixture的范围从简单的单元扩展到复杂的功能测试,允许根据配置和组件选项对fixture和测试用例进行参数化,或者跨函数function、类class、模块module或整个测试会话sessio范围。
二、用途:
1.做测试前后的初始化设置,如测试数据准备,链接数据库,打开浏览器等这些操作都可以使用fixture来实现
2.测试用例的前置条件可以使用fixture实现
3.支持经典的xunit fixture ,像unittest使用的setup和teardown
4.fixture可以实现unittest不能实现的功能,比如unittest中的测试用例和测试用例之间是无法传递参数和数据的,但是fixture却可以解决这个问题
三、fixture作为参数传入:
1、定义fixture跟定义普通函数差不多,唯一区别就是在函数上加个装饰器@pytest.fixture(),fixture命名不要用test_开头,跟用例区分开。用例才是test_开头的命名。
2、fixture是可以有返回值的,如果没return默认返回None。用例调用fixture的返回值,直接就是把fixture的函数名称当成变量传入
3、fixture装饰器里的scope有四个级别的参数。“function(不写默认这个)”,“class”,“module”or“session”
4、除scope之外。还有params、autouse、ids、name等。
5、fixture可以有返回值,如果没有return,默认会是None;用例调用fixture的返回值,就是直接把fixture的函数名称作为参数传入
6、fixture可以返回一个元组、列表或字典
7、test_用例可传单个、多个fixture参数
8、fixture与fixture间可相互调用
fixture可以返回一个元组、列表或字典
@pytest.fixture()
def user():
print("=====打印装饰器函数=====")
username='wangli'
psw=123456
assert username=="wangli"
assert psw == 123456
return username,psw
def test_login(user):
print(user)
assert user[0]=="wangli"
assert user[1] == 123456
if __name__=="__main__":
pytest.main(["-s","test03.py"])
"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/test/test/test03.py
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-5.1.2, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\wangli\PycharmProjects\test\test
collected 1 item
test03.py =====打印装饰器函数=====
('wangli', 123456)
.
============================== 1 passed in 0.16s ==============================
Process finished with exit code 0
test_用例可传单个、多个fixture参数
@pytest.fixture()
def username():
print("=====打印username装饰器函数=====")
username='wangli'
assert username=="wangli"
return username
@pytest.fixture()
def psw():
print("=====打印psw装饰器函数=====")
psw=123456
assert psw==123456
return psw
def test_login(username,psw):
print(username,psw)
assert username=="wangli"
assert psw == 123456
if __name__=="__main__":
pytest.main(["-s","test03.py"])
"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/test/test/test03.py
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-5.1.2, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\wangli\PycharmProjects\test\test
collected 1 item
test03.py =====打印username装饰器函数=====
=====打印psw装饰器函数=====
wangli 123456
.
============================== 1 passed in 0.13s ==============================
Process finished with exit code 0
fixture与fixture间可相互调用
@pytest.fixture()
def first():
print("=====first=====")
username='wangli'
assert username=="wangli"
return username
@pytest.fixture()
def sencond(first):
print("=====sencond=====")
psw=123456
assert psw==123456
return first,psw
def test_login(sencond):
print(sencond)
assert sencond[0]=="wangli"
assert sencond[1] == 123456
if __name__=="__main__":
pytest.main(["-s","test03.py"])
"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/test/test/test03.py
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-5.1.2, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\wangli\PycharmProjects\test\test
collected 1 item
test03.py =====first=====
=====sencond=====
('wangli', 123456)
.
============================== 1 passed in 0.02s ==============================
Process finished with exit code 0
四、fixture源码详解:
fixture(scope='function',params=None,autouse=False,ids=None,name=None):
fixture里面有个scope参数可以控制fixture的作用范围,scope:有四个级别参数"function"(默认),"class","module","session
params:一个可选的参数列表,它将导致多个参数调用fixture功能和所有测试使用它。
autouse:如果True,则为所有测试激活fixture func可以看到它。如果为False则显示需要参考来激活fixture
ids:每个字符串id的列表,每个字符串对应于params这样他们就是测试ID的一部分。如果没有提供ID它们将从params自动生成
name:fixture的名称。这默认为装饰函数的名称。如果fixture在定义它的统一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽,解决这个问题的一种方法时将装饰函数命令"fixture_
五、fixture的作用范围
fixture里面有个scope参数可以控制fixture的作用范围:session>module>class>function
-function:每一个函数或方法都会调用
-class:每一个类调用一次,一个类中可以有多个方法
-module:每一个.py文件调用一次,该文件内又有多个function和class
-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module
函数级别的,每一个函数或方法都会调用,每个测试用例执行前都会执行一次function级别的fixture
@pytest.fixture()如果不写参数,参数就是scope="function",它的作用范围是每个测试用例来之前运行一次,销毁代码在测试用例之后运行。
如果一个class里面有多个用例,都调用了1次fixture,那么此fixture只在此class里所有用例开始前执行一次。
@pytest.fixture(scope='class')
def test1():
b = '男'
print('传出了%s, 且只在class里所有用例开始前执行一次!!!' % b)
return b
class TestCase:
def test3(self, test1):
name = '男'
print('找到name')
assert test1 == name
def test4(self, test1):
sex = '男'
print('找到sex')
assert test1 == sex
if __name__=="__main__":
pytest.main(["-s","test03.py"])
"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/test/test/test03.py
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-5.1.2, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\wangli\PycharmProjects\test\test
collected 2 items
test03.py 传出了男, 且只在class里所有用例开始前执行一次!!!
找到name
.找到sex
.
============================== 2 passed in 0.09s ==============================
Process finished with exit code 0
每一个.py文件调用一次,fixture为module时,在当前.py脚本里面所有用例开始前只执行一次,模块里所有的用例执行前执行一次module级别的fixture
@pytest.fixture(scope='module')
def test1():
b = '男'
print('传出了%s, 且在当前py文件下执行一次!!!' % b)
return b
def test3(test1):
name = '男'
print('找到name')
assert test1 == name
class TestCase:
def test4(self, test1):
sex = '男'
print('找到sex')
assert test1 == sex
if __name__=="__main__":
pytest.main(["-s","test03.py"])
"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/test/test/test03.py
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-5.1.2, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\wangli\PycharmProjects\test\test
collected 2 items
test03.py 传出了男, 且在当前py文件下执行一次!!!
找到name
.找到sex
.
============================== 2 passed in 0.09s ==============================
Process finished with exit code 0
1、可以跨.py文件调用,有多个.py文件调用时,实际只调用了一次fixture
2、conftest.py与运行的用例要在同一个pakage下,并且有__init__.py文件
3、不需要import导入 conftest.py,pytest会自动识别该文件,放到项目的根目录下就可以全局调用了,如果放到某个package下,那就在改package内有效
4、conftest.py配置脚本名称是固定的,不能改名称
多个.py文件只调用1次fixture
import pytest
# conftest.py
@pytest.fixture(scope='session')
def get_token():
token = 'qeehfjejwjwjej11sss@22'
print('获取到token:%s' % token)
return token
import pytest
# test02.py
class Test(object):
def test2(self,get_token):
token = 'qeehfjejwjwjej11sss@22'
print("【执行test02.py-Test类-test2用例,获取get_token:%s】" %get_token)
assert get_token == token
if __name__=="__main__":
pytest.main(["-s","test02.py","test03.py"])
import pytest
#test03.py
class Test(object):
def test3(self,get_token):
token = 'qeehfjejwjwjej11sss@22'
print("【执行test03.py-Test类-test3用例,获取get_token:%s】" %get_token)
assert get_token == token
def test4(self,get_token):
token = 'qeehfjejwjwjej11sss@22'
print("【执行test03.py-Test类-test4用例,获取get_token:%s】" %get_token)
assert get_token == token
if __name__=="__main__":
pytest.main(["-s","test02.py","test03.py"])
"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/Test/test/test02.py
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-5.1.2, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\wangli\PycharmProjects\Test\test
collected 3 items
test02.py 获取到token:qeehfjejwjwjej11sss@22
【执行test02.py-Test类-test2用例,获取get_token:qeehfjejwjwjej11sss@22】
.
test03.py 【执行test03.py-Test类-test3用例,获取get_token:qeehfjejwjwjej11sss@22】
.【执行test03.py-Test类-test4用例,获取get_token:qeehfjejwjwjej11sss@22】
.
============================== 3 passed in 0.30s ==============================
Process finished with exit code 0
多个.py文件只调用多次fixture
import pytest
# conftest.py
@pytest.fixture()
def get_token():
token = 'qeehfjejwjwjej11sss@22'
print('获取到token:%s' % token)
return token
import pytest
# test02.py
class Test(object):
def test2(self,get_token):
token = 'qeehfjejwjwjej11sss@22'
print("【执行test02.py-Test类-test2用例,获取get_token:%s】" %get_token)
assert get_token == token
if __name__=="__main__":
pytest.main(["-s","test02.py","test03.py"])
import pytest
#test03.py
class Test(object):
def test3(self,get_token):
token = 'qeehfjejwjwjej11sss@22'
print("【执行test03.py-Test类-test3用例,获取get_token:%s】" %get_token)
assert get_token == token
def test4(self,get_token):
token = 'qeehfjejwjwjej11sss@22'
print("【执行test03.py-Test类-test4用例,获取get_token:%s】" %get_token)
assert get_token == token
if __name__=="__main__":
pytest.main(["-s","test02.py","test03.py"])
"C:\Program Files\Python35\python.exe" C:/Users/wangli/PycharmProjects/Test/test/test03.py
============================= test session starts =============================
platform win32 -- Python 3.5.2, pytest-5.1.2, py-1.8.0, pluggy-0.12.0
rootdir: C:\Users\wangli\PycharmProjects\Test\test
collected 3 items
test02.py 获取到token:qeehfjejwjwjej11sss@22
【执行test02.py-Test类-test2用例,获取get_token:qeehfjejwjwjej11sss@22】
.
test03.py 获取到token:qeehfjejwjwjej11sss@22
【执行test03.py-Test类-test3用例,获取get_token:qeehfjejwjwjej11sss@22】
.获取到token:qeehfjejwjwjej11sss@22
【执行test03.py-Test类-test4用例,获取get_token:qeehfjejwjwjej11sss@22】
.
============================== 3 passed in 0.04s ==============================
Process finished with exit code 0