一、pytest之跳过用例 @pytest.mark.skip和@ pytest.mark.skipif
1-1、@pytest.mark.skip (无条件跳过,只要使用了这个关键字就会跳过)
参数:reason(跳过的原因)
该参数非必传,可传可不传
代码:
import pytest
class TestIndex():
@pytest.mark.skip(reason='跳过该用例')
def test_myself(self):
print '我被跳过了'
def test_class(self):
print '我没被跳过'
运行结果:
1-2、@pytest.mark.skipif(有条件跳过,只有符合条件才会跳过)
参数:
reason(跳过的原因)该参数非必传,可传可不传
condition (跳过条件),只有当该条件结果为True时才会跳过该用例
代码:
import pytest
class TestIndex():
@pytest.mark.skipif(condition=2>1,reason='因为2>1为真,所以跳过该用例')
def test_myself(self):
print '我被跳过了'
def test_class(self):
print '我没被跳过'
运行结果:
1-3、除了上面两个注解以外,还可以通过调用pytest.skip()来跳过用例
代码:
import pytest
class TestIndex():
def test_myself(self):
pytest.skip(msg='跳过该用例')
print '我被跳过了'
def test_class(self):
print '我没被跳过'
运行结果:
跳过用例类或用例函数的方法一样,使用1或2两个注解修饰即可
跳过模块则需要使用变量pytestmark,让它等于标签
import pytest
pytestmark=pytest.mark.skipif(condition=2>1,reason='因为2>1为真,所以跳过该用例')
class TestIndex():
def test_myself(self):
print '我被跳过了'
def test_class(self):
print '我没被跳过'
运行结果:
二、 标签@pytest.mark.parametrize()详解:
该标签是一种参数化方式,pytest有两种参数化方式,除了此标签外,还有一种是@pytest.fixture(params=[]),这个在这里不做解释,在后面fixture篇会详细说明
接下来解释说明@pytest.mark.parametrize()的各个参数
2-1、argnames、argvalues
必传参数,argnames定义参数名,argvalues为参数值
argnames数量与argvalues数量一一对应
用法:
2-1-1、单参数单值
代码:
import pytest
class TestIndex():
@pytest.mark.parametrize(argnames='arg',argvalues=['我是参数arg'])
def test_myself(self, arg):
print arg
运行结果:
2-1-2、单参数多值
代码:
import pytest
class TestIndex():
@pytest.mark.parametrize(argnames='arg',argvalues=['我是参数arg',(1),['arg']])
def test_myself(self, arg):
print arg
运行结果:
标签中设置了三组值,用例执行了三次,这就是pytest 的数据驱动,参数值有n组,用例就会遍历执行n次
2-1-3、多参数多值
代码:
import pytest
class TestIndex():
@pytest.mark.parametrize(argnames="arg1,arg2",
argvalues=[("我是第一个参数",'我是第二个参数'),('第二次第一个参数','第二次第二个参数')])
def test_myself(self, arg1, arg2):
print arg1+'、'+arg2
运行结果:
这里多参数时,参数值是一个元组,一个元组是一组参数
2-2、 indirect
1),indirect一般与pytest的request、fixture组合使用
2),当indirect为true时,argnames则要传入fixture的函数名,不再是普通参数,而是要被调用的fixture函数,argvalues则是要给这个参数传的值
3),作用其实与pytest.fixture(params)一样,但使用了@pytest.mark.parametrize相当于参数化了fixture
2-2-1.单fixture单值(列表形式传值)
代码:
import pytest
class TestIndex():
@pytest.fixture()
def qianzhi(self,request): #request是pytest的固定用法,用于接收传参并调用
user = request.param
print '传入的用户名:%s' % user
return user #如果想fixture有返回值,直接return即可,这个返回值可以在调用
#它的测试函数中使用
user = ['张三','李四']
@pytest.mark.parametrize('qianzhi', user, indirect=True) # 这里的'qianzhi'就是调用的
# fixture的函数名,user为传递
# 给fixture的值
def test_myself(self, qianzhi): # 这里入参的qianzhi就是fixture的调用,便且可以拿到
# fixture的返回值,如果fixture有返回值
print '测试类接收的用户名:%s' % qianzhi
运行结果:
2-2-2.单fixture多值(字典形式传值)
代码:
import pytest
class TestIndex():
@pytest.fixture()
def qianzhi(self,request):
user = request.param
print '传入的用户名:%s,密码:%s' % (user['user'],user['pwd'])
return user
user = [
{'user':'张三','pwd': '123'},
{'user':'李四','pwd': '456'}]
@pytest.mark.parametrize('qianzhi', user, indirect=True)
def test_myself(self, qianzhi):
print '测试类接收的用户名:%s,密码:%s' % (qianzhi['user'],qianzhi['pwd'])
运行结果:
2-2-3. 多fixture多值(嵌入元组的列表形式传参)
代码:
import pytest
class TestIndex():
@pytest.fixture()
def login(self,request):
user = request.param
print '传入的用户名:%s' %user
return user
@pytest.fixture()
def pwd(self,request):
user = request.param
print '传入的密码:%s' % user
return user
user = [
('张三','123'),
('李四','456')]
@pytest.mark.parametrize("login,pwd", user, indirect=True)
def test_myself(self, login,pwd):
print '测试类接收的用户名:%s,密码:%s' % (login,pwd)
运行结果:
2-2-4.叠加fixture(单值列表,执行次数是笛卡尔积N*M)
代码:
import pytest
class TestIndex():
@pytest.fixture()
def login_user(self,request):
user = request.param
print '传入的用户名:%s' %user
return user
@pytest.fixture()
def login_pwd(self,request):
pwd = request.param
print '传入的密码:%s' % pwd
return pwd
user = ['张三','李四']
pwd = ['123','456']
@pytest.mark.parametrize("login_user", user, indirect=True)
@pytest.mark.parametrize("login_pwd", pwd, indirect=True)
def test_myself(self, login_user,login_pwd):
print '测试类接收的用户名:%s,密码:%s' % (login_user,login_pwd)
运行结果:
可以看到一共执行了四次,也就是每个fixture传入值的个数的积
总结:当indirect为True时就是调用fixture,将参数传入fixture做一次前置处理,为False时就是普通的参数化
2-3、ids(用来设置标题)
不设置ids的情况下,pytest执行后会用输入数据作为用例标题显示
使用ids可以自定义用例标题
但注意,使用中文时 会显示unicode编码
解决方法:
在test_case同级目录新建conftest.py文件,在文件中添加以下代码:
from typing import List
def pytest_collection_modifyitems(
session: "Session", config: "Config", items: List["Item"]
) -> None:
for item in items:
item.name = item.name.encode('utf-8').decode('unicode-escape')
item._nodeid = item.nodeid.encode('utf-8').decode('unicode-escape')
2-4、scope
scope的作用范围取值与fixture 的scope一致,当indirect=True才会被使用
scope的作用范围会覆盖fixture的scope范围,如果同一个被调用的fixture有多个parametrize定义了scope,取第一条的范围,见下例:
2-4-1.小于Fixture的scope范围:
2-4-2.大于Fixture的scope范围:
2-4-3.多个parametrize不同的scope范围,为先执行的范围:
2-4-4.与mark变量一起使用,标记子用例
代码:
import pytest
class TestIndex():
@pytest.mark.parametrize('x,y',[('test','123'),
pytest.param('test1','1234',marks=pytest.mark.xfail)])
def test_myself(self,x,y):
print '用户名:%s 密码:%s' % (x,y)
运行结果:
三、pytest.mark.自定义标签使用
可以标记测试方法、测试类,标记名可以自定义,最好起有意义的名字
同一测试类/方法可同时拥有多个标记
3-1,实例:
import pytest
@pytest.mark.login
class TestIndex():
@pytest.mark.test
def test_myself(self):
print '示例'
@pytest.mark.start
def test_myself(self):
print 'kaishi'
3-2、运行方法;
pytest -m "自定义标签名"
或:
pytest.main(['-m 自定义标签名'])
3-3、 组合运行用例:
使用-m参数运行标记的用例,-m参数支持and、or、not等表达式
3-4、 注册、管理mark标记:
当使用 -m 参数执行 mark 标记的用例时,pytest 会发出告警信息 “PytestUnknownMarkWarning: Unknown pytest.mark.login - is this a typo? ”,告诉你这是一个 pytest 未知的一个标记!为了消除告警,我们需要在 pytest 的配置文件中注册 mark 标记!
注册mark标记:
首先在项目根目录创建一个文件 pytest.ini ,这个是 pytest 的配置文件
然后在 pytest.ini 文件的markers 中写入你的 mark 标记, 冒号 “:” 前面是标记名称,后面是 mark 标记的说明,可以是空字符串
注意:pytest.ini 文件中只能使用纯英文字符,绝对不能使用中文的字符(尤其是冒号和空格)
规范使用mark标记:
注册完 mark 标记之后 pytest 便不会再告警,但是有时手残容易写错 mark 名,导致 pytest 找不到用例,一时想不开很难debug,尤其是团队协作时很容易出现类似问题
处理方法:在 pytest.ini 文件中添加参数 “addopts = --strict”
添加该参数后,当使用未注册的 mark 标记时,pytest会直接报错:“ ‘xxx’ not found in markers configuration option ”,不执行测试任务
注意:pytest.ini 配置文件不支持注释,不支持注释,不支持注释…