pytest关键字详解

一、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 '我没被跳过'

运行结果:

pytest关键字详解_第1张图片

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 '我没被跳过'

运行结果:

pytest关键字详解_第2张图片

 1-3、除了上面两个注解以外,还可以通过调用pytest.skip()来跳过用例

代码:

import pytest
class TestIndex():
    def test_myself(self):
        pytest.skip(msg='跳过该用例')
        print '我被跳过了'

    def test_class(self):
        print '我没被跳过'

 运行结果:

pytest关键字详解_第3张图片

 跳过用例类或用例函数的方法一样,使用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关键字详解_第4张图片

二、 标签@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

运行结果:

pytest关键字详解_第5张图片

2-1-2、单参数多值

代码:

import pytest
class TestIndex():
    @pytest.mark.parametrize(argnames='arg',argvalues=['我是参数arg',(1),['arg']])
    def test_myself(self, arg):
        print arg

运行结果:

pytest关键字详解_第6张图片

标签中设置了三组值,用例执行了三次,这就是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

 运行结果:

pytest关键字详解_第7张图片

这里多参数时,参数值是一个元组,一个元组是一组参数

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

运行结果:

pytest关键字详解_第8张图片

 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'])

运行结果:

 pytest关键字详解_第9张图片

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)

运行结果:

 pytest关键字详解_第10张图片

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)

运行结果:

 pytest关键字详解_第11张图片

可以看到一共执行了四次,也就是每个fixture传入值的个数的积

总结:当indirect为True时就是调用fixture,将参数传入fixture做一次前置处理,为False时就是普通的参数化

 2-3、ids(用来设置标题)

不设置ids的情况下,pytest执行后会用输入数据作为用例标题显示

使用ids可以自定义用例标题

pytest关键字详解_第12张图片

但注意,使用中文时 会显示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范围:

pytest关键字详解_第13张图片

 2-4-2.大于Fixture的scope范围:

pytest关键字详解_第14张图片

  2-4-3.多个parametrize不同的scope范围,为先执行的范围:

 pytest关键字详解_第15张图片

 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关键字详解_第16张图片

 三、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 自定义标签名'])

pytest关键字详解_第17张图片

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 标记!

pytest关键字详解_第18张图片

注册mark标记:

首先在项目根目录创建一个文件 pytest.ini ,这个是 pytest 的配置文件

 然后在 pytest.ini 文件的markers 中写入你的 mark 标记, 冒号 “:” 前面是标记名称,后面是 mark 标记的说明,可以是空字符串

 注意:pytest.ini 文件中只能使用纯英文字符,绝对不能使用中文的字符(尤其是冒号和空格)

pytest关键字详解_第19张图片

 规范使用mark标记:

注册完 mark 标记之后 pytest 便不会再告警,但是有时手残容易写错 mark 名,导致 pytest 找不到用例,一时想不开很难debug,尤其是团队协作时很容易出现类似问题

处理方法:在 pytest.ini 文件中添加参数 “addopts = --strict”

 添加该参数后,当使用未注册的 mark 标记时,pytest会直接报错:“ ‘xxx’ not found in markers configuration option ”,不执行测试任务pytest关键字详解_第20张图片

 注意:pytest.ini 配置文件不支持注释,不支持注释,不支持注释…

 

 

你可能感兴趣的:(pytest,python编程,python)