在做自动化的过程中,编写用例时候需要用到用例的前置和用例的后置,其中pytest中有setup_class和teardown_class可以帮助我们完成这些,但是不够完善而且灵活性不够强。举个简单的例子,一个calss中有3条用例,其中2条需要登录,1条不需要登录,这个时候如果在用setup和teardown来做就有点不方便。这个时候就引入了新的知识点fixture。
fixture属于pytest中的一个方法。fixture可以用作测试用例的前置和后置操作,其中fixture命令规范没有像setup和teardown固定格式。可以随意命名。控制fixture的前置和后置操作是通过yield关键字进行来区分的,代码在yield前面的属于前置操作,代码在yield后面的属于后置操作。并且fixture也没有强烈的要求必须要前后置同时存在,可以只存在前置也可以只存在后置。fixture如果有后置内容,无论遇到什么问题,都会进行执行后置的代码。
return和yeild的区别: 都是表示返回值的意思,但是return的后面不能有代码,yeild很后面可以有代码
yield语句之前的就会在测试用例之前使用,yield之后的语句就会在测试用例执行完成之后再执行。
常见的应用场景:
- @pytest.fixture可以用在selenium中测试用例执行前后打开、关闭浏览器的操作; yield返回driver对象去操作浏览器;
- @pytest.fixture可以用在接口自动化测试中操作登录,yield返回token;
fixture中一共有5个参数分别是:scope,params,autouse,ids,name
前面已经介绍了fixture是干什么的,但是具体的用法没有说,先介绍下具体用法,这里通过将登录操作写在一个函数中。然后通过装饰器的方法进行使其变成fixture。那条用例需要用到对应的前置和后置操作的时,直接将fixture的名称通过传参的形式放到用例中。
一个class中存在3条用例,其中2条需要登录,1条不需要登录。运营fixture进行完成前后置操作。
import pytest
@pytest.fixture()
def login():
print("log In")
yield "Token:xxxxxxxxxxxxxxxxx" #yield之前的内容是setup的相关内容,之后的内容是teardown的操作
print("Log out")
class TestFixture:
def test_case001(self,login):
assert 1==1
def test_case002(self):
assert 1==1
def test_case003(self,login):
assert 2==1
通过查看执行后的结果发现,login的fixture已经在test_001和test_003中进行使用到了。test_002没有用到登录。这里也没有给他传入,对应的结果也是没用登录。
前面提到过用例出现异常后,fixture继续执行后置操作。这里安静也距离给大家说明。
将上面的代码用例中加入断言内容,保证用例3断言失败,看看是否会执行后置操作(退出登录)。
通过执行发现,我们的test_003断言失败后,继续执行了退出登录操作。也就是后置内容。
fixture不仅仅能单个使用,还能一起使用。使用的执行顺序取决于你传入的顺序决定,意思就是,先传的先执行。
import pytest
@pytest.fixture()
def login():
print("login begin")
yield "Token:xxxxxxxxxxxxxxxxx"
print("login end")
@pytest.fixture()
def logFunc():
print("logFunc begining")
yield
print("logFunc finished")
class TestFixture:
#先login 在执行log
def test_case001(self,login,logFunc):
assert 1==1
def test_case002(self):
assert 1==1
#先log 在执行login
def test_case003(self,logFunc,login):
assert 2==2
if __name__ == '__main__':
pytest.main()
通过下图执行结果可以看出,test_001先执行的前置操作是login的,test_003先执行的前置操作是logFunc的。
你以为fixture的功能就这么多了?当然不是了,fixture还可以进行其他fixture的调用操作。
调用方法:直接将被调用fixture的函数名放到调用的fixture当做参数传入。
import pytest
@pytest.fixture()
def login():
print("登录操作")
yield "Token:xxxxxxxxxxxxxxxxx"
print("退出登录")
@pytest.fixture()
def logFunc(login):# log调用login功能
"""
打开日志前需要先登录
:param login:
:return:
"""
print("Log begining")
yield
print("log finished")
class TestFixture:
def test_case001(self,login,logFunc):
assert 1==1
def test_case002(self):
assert 1==1
def test_case003(self,logFunc,login):
assert 1==1
if __name__ == '__main__':
pytest.main()
这里只需要传入一个参数即刻。通过执行发现,已经成功的调用了2个fixture的内容。
上面介绍了如何使用fixture,以及fixture的之间的相互调用。前面也留下来一个问题就是关于fixture的5个参数都表示什么意思。5个参数分别是name,scope,params,autouse,ids,。安静一个个给大家解释。
name参数表示可以对fixture的名称进行重命名。
import pytest
@pytest.fixture(name="Test123")
def login():
print("登录操作")
yield "Token:xxxxxxxxxxxxxxxxx"
print("退出登录")
@pytest.fixture()
def logFunc():
"""
打开日志前需要先登录
:param login:
:return:
"""
print("Log begining")
yield
print("log finished")
class TestFixture:
def test_case001(self,Test123,logFunc):
assert 1==1
def test_case002(self):
assert 1==1
def test_case003(self,logFunc,Test123):
assert 1==1
if __name__ == '__main__':
pytest.main()
执行后发现,通过参数name更改过的名字可以直接进行调用。
注意:通过name重命名后,继续使用以前的名字调用会报错。
fixture中的参数scope是用于控制fixture的作用范围。
scope中控制的还有其他的几种参数,“session”,“package”,“module”,“class”,“function”。
其中scope默认参数为function,表示应用于单个测试函数中。
session:主要应用于会话级别的fixture。
module:主要应用于模块级别的fixture(单个.py文件)。
class:主要应用于class中,表示每个前置和后置都会执行一次。
package:主要应用于每个包下的fixture。
执行顺序:session>>module>>class>>function
import pytest
@pytest.fixture(scope='session')
def fix_session():
print("这是session")
yield
print("session 结束")
@pytest.fixture(scope='class')
def fix_class():
print("这是class")
yield
print("class 结束")
@pytest.fixture(scope='module')
def fix_module():
print("这是module")
yield
print("module 结束")
@pytest.fixture(scope='function')
def fix_function():
print("这是function")
yield
print("function 结束")
class TestFixture:
def test_case001(self,fix_function,fix_class,fix_module,fix_session):
assert 1==1
if __name__ == '__main__':
pytest.main()
autouse表示在fixture中用于控制fixture作用范围内的是否全部执行。默认为False,当设置为True的时候表示该fixture在当前作用范围内,全部执行。
import pytest
@pytest.fixture(autouse=True)
def login():
print('\n登录操作')
yield
print('\n退出登录!')
class Test_Login():
def test_01(self):
print('---用例01---')
def test_02(self):
print('---用例02---')
def test_03(self):
print('---用例03---')
if __name__ == '__main__':
pytest.main(['-vs'])
params表示fixture的参数化功能。参数化这里就不用多说了吧。
这里会有一个request参数,主要用来接收fixture的返回结果。并通过request.param返回结果内容。
import pytest
data = ['user01','user02','user03']
@pytest.fixture(params=data)
def login(request):
print('\n登录操作')
yield request.param
print('\n退出登录!')
class Test_Login():
def test_01(self,login):
print('---用例01---')
print(f'登录的用户名:{login}')
def test_02(self):
print('---用例02---')
def test_03(self):
print('---用例03---')
if __name__ == '__main__':
pytest.main(['-vs'])
这里只需要传入一个参数即可。通过执行发现,已经成功的调用了2个fixture的内容。
ids表示在fixture对参数化的内容进行加上标识,比如让别人知道这个传入的参数是什么意思。用于什么样的测试用例。默认是传参数内容:
import pytest
data = ['user01','user02','user03']
@pytest.fixture(params=data,ids=['user=user01','user=user02','user=user03'])
def login(request):
print('\n登录操作')
yield request.param
print('\n退出登录!')
class Test_Login():
def test_01(self,login):
print('---用例01---')
print(f'登录的用户名:{login}')
def test_02(self):
print('---用例02---')
def test_03(self):
print('---用例03---')
if __name__ == '__main__':
pytest.main(['-vs'])
执行结果可以看到在每条用例的后面都会有对应的参数详解。