Pytest测试框架(四)---装饰器的使用

目录

1、Fixture装饰器

1.1、fixture的conftest.py文件简介与使用

1.1.1、简介

1.1.2、使用示例

1.2.3、conftest.py的作用域

1.2、多个fixture

 1.3、fixture的相互调用

1.4、fixture的作用范围scope

1.4.1、简介

1.4.2、scope中class和function的作用域区别

1.4.3、scope中session和module的作用域的区别

1.5、fixture的用例管理

1.5.1、跳过用例skip/skipif

1.5.2、标记失败xfail

1.5.3、重复执行repeat

1.6、fixture的断言机制

1.6.1、常用断言

1.7、使用pytest.ficture装饰器携带参数

1.7.1、使用测试固件对象中的参数params

1.7.1、解析yaml文件传递数据

1.8、mark自定义标记

1.8.1、自定义标记测试用例,并按标记执行用例


1、Fixture装饰器

1.1、fixture的conftest.py文件简介与使用

1.1.1、简介

       conftest.py文件:是pytest框架中非常重要的一个东西,可以实现fixture对象自对应并自动应用完成跨模版、跨文件的应用操作,从而使fixture对象的定义更加灵活、方便;

1.1.2、使用示例

1、创建一个目录demo7,在该目录下创建两个目录(test_case1和test_case2)和一个文件(congtest.py);目录test_1创建测试文件(test_1.py和test_2.py),test_2中创建测试文件(test_3.py);

2、文件conftest.py内容:

import pytest

@pytest.fixture()
def get_data():
    a = '-------1-------'
    b = 2
    print(a)
    return b

 3、文件test_1.py内容:(test_1_1和test_1_3引用conftest.py文件总的get_data函数)

import  pytest
def test_1_1(get_data):
    sun = 1+get_data
    print(sun)
    print('-----test_1_1----')

def test_1_2():
    print('-----test_1_2----')


def test_1_3(get_data):
    sun = 2+get_data
    print(sun)
    print('-----test_1_3----')
if __name__ == '__main__':
    pytest.main()

4、文件test_2.py内容:(test_2_1引用conftest.py文件总的get_data函数)

import  pytest
def test_2_1(get_data):
    sun = 10+get_data
    print(sun)
    print('-----test_2-1----')

def test_2_2():
    print('-----test_2-2----')


def test_2_3():
    print('-----test_2-3----')
if __name__ == '__main__':
    pytest.main()

5、文件test_3.py文件内容:(test_3_1和test_3_3引用conftest.py文件总的get_data函数)

import  pytest
def test_3_1(get_data):
    sun = 100+get_data
    print(sun)
    print('-----test_3_1----')

def test_3_2():
    print('-----test_3_2----')


def test_3_3(get_data):
    sun = 200+get_data
    print(sun)
    print('-----test_3_3----')
if __name__ == '__main__':
    pytest.main(['-s', ['test_3.py']])

 6、执行三个测试文件(执行demo7目录下所有的测试文件)

7、测试结果:

通过结果可以看出跨目录跨文件引用了conftest.py中的get_data函数

1.2.3、conftest.py的作用域

1、在一个测试工程中可以存在多个conftest.py文件,如果存在多个,则会根据conftest.py所在位置,作用域也不同;在根工程目录中,则该文件起全局作用,如果在子目录中,则该文件只作用于该文件下的对象及当前层级下的子目录有效;

2、下面有以下情况:

2.1、在test_case1目录下增减一个conftest.py文件:(该文件里的函数名和根工程目录demo7)下的conftest.py里的函数名相同

2.2、文件内容:

import pytest

@pytest.fixture()
def get_data():
    a = '-------data2-------'
    b = 2
    print(a)
    return b

2.3、再执行demo7下所有测试文件,测试结果如下:(test_case1里的测试文件引用的是自己目录下的conftest.py里的函数)

2.4、总结:根工程目录(demo7)conftest.py里的函数名和子目录(test_case1)conftest.py里的函数名相同时,test_case1目录里的测试文件优先使用自己目录下conftest.py;且只作用于当前层级及子目录下对象即不作用于test_case2;test_case2使用的是demo7目录下的conftest.py(作用于全局);

3.1、在test_case1目录下增减一个conftest.py文件:(该文件里的函数名和根工程目录demo7)下的conftest.py里的函数名不相同)test_case1下文件test_1.py同时使用两个函数;

3.2、修改test_1.py如下:

import  pytest
def test_1_1(get_data):   # 引用根工程目录(demo7)下的conftest.py的函数
    sun = 1+get_data
    print(sun)
    print('-----test_1_1----')

def test_1_2():
    print('-----test_1_2----')


def test_1_3(get_data2):  # 引用test_case1下的conftest.py的函数
    sun = 2+get_data2
    print(sun)
    print('-----test_1_3----')
if __name__ == '__main__':
    pytest.main()

 3.3、执行test_1.py文件,结果如下:

1.2、多个fixture

1、conftest.py文件示例代码如下:

import pytest

@pytest.fixture()
def get_data1():
    a = '-------100-------'
    b = 20
    print(a)
    return b


@pytest.fixture()
def get_data2():
    a = '-------200-------'
    c = 30
    print(a)
    return c

 2、测试文件test_4.py如下:

import  pytest
def test_3_1(get_data1, get_data2):
    sun = get_data1+get_data2
    print(sun)
if __name__ == '__main__':
    pytest.main(['-s', 'test_4.py'])

3、执行test_4.py文件结果如下:

 1.3、fixture的相互调用

1、conftest.py文件示例代码如下:(data2调用data1)

import pytest

@pytest.fixture()
def data1():
    print('----data1---')
    a = 1
    return a


@pytest.fixture()
def data2(data1):    # 调用data1的数据
    print('----data2---')
    b = 2+data1
    return b

2、测试文件test_1.py如下:

import pytest

def test_1(data2):
    sum = 100+data2
    print(sum)
    assert sum==103

if __name__ == '__main__':
    pytest.main()

3、执行结果:

1.4、fixture的作用范围scope

1.4.1、简介

1、fixture中有一个参数scope,其可以控制fixture对象的作用范围,其值有session、module、class、function、package等

1.4.2、scope中class和function的作用域区别

1、scope中function相当于函数级别的setup_function和类级别的setup_method

2、下面测试下scope中class和function的区别:

2.1、scope=‘class’,示例代码如下:

import pytest

@pytest.fixture(scope='class')
# @pytest.fixture(scope='function')
def data1():
    a = 2
    print('----data1----')
    return a

def test_1(data1):
    sum = 1 + data1
    print(sum)
    print('----test1----')

class Test_1():
    def test_2(self, data1):
        sum = 2 + data1
        print(sum)
        print('---test2_1---')

    def test_1(self, data1):
        sum = 3+data1
        print(sum)
        print('---test2_2---')

def test_2(data1):
    sum = 4+data1
    print(sum)
    print('----test2----')

if __name__ == '__main__':
    pytest.main()

 2.2、执行结果:(class类型数据只执行一次data1,但数据可以传递给类里的所有函数;函数类型用例,都会执行一次)

3.1、scope=’function’,示例代码如下:

import pytest

# @pytest.fixture(scope='class')
@pytest.fixture(scope='function')
def data1():
    a = 2
    print('----data1----')
    return a

def test_1(data1):
    sum = 1 + data1
    print(sum)
    print('----test1----')

class Test_1():
    def test_2(self, data1):
        sum = 2 + data1
        print(sum)
        print('---test2_1---')

    def test_1(self, data1):
        sum = 3+data1
        print(sum)
        print('---test2_2---')

def test_2(data1):
    sum = 4+data1
    print(sum)
    print('----test2----')

if __name__ == '__main__':
    pytest.main()

3.2、执行结果如下:(作用于函数,类型测试用例里的所有函数都会执行一次;函数型用例,也都会执行一次)

4、总结:function和class的区别:两者如果作用于测试函数,则两者都是等价的,都表示创建全新的固件对象;如果针对的是类中的测试方法,function表示每个测试方法创建全新的对象,而class表示测试类中所有测试用例共用一个测试固件;

1.4.3、scope中session和module的作用域的区别

1、session和module的区别:module表示针对模块而言,每个模块中使用的是同一个固件对象,不同的模块创建的固件对象是不同的;而session表示整个会话,不同模块使用的对象也是不同的;

1.5、fixture的用例管理

1.5.1、跳过用例skip/skipif

1、@pytest.mark.skip(reason='不执行')

2、测试文件:

import pytest

@pytest.mark.skip(reason='不执行')
def test_01():
    print('---test1---')

def test_02():
    print('---test2---')

if __name__ == '__main__':
    pytest.main()

3、执行结果:

4、@pytest.mark.skipif(2<3,reason='跳过用例');测试文件:

import pytest

@pytest.mark.skipif(2<3,reason='跳过用例')
def test_01():
    print('---test1---')

def test_02():
    print('---test2---')

if __name__ == '__main__':
    pytest.main()

 5、执行结果:

1.5.2、标记失败xfail

1、代码示例:

import pytest

def test_01():
    print('---test1---')

@pytest.mark.xfail(reason='跳过')
def test_02():
    print('---test2---')
    assert 1==2

if __name__ == '__main__':
    pytest.main()

2、执行结果:

1.5.3、重复执行repeat

1、先安装pytest-repeat库;执行pip install pytest-repeat

2、示例代码:(通过mark标记的方法指定测试用例执行的次数

import pytest

def test_01():
    print('---test1---')
@pytest.mark.repeat(5)
def test_02():
    print('---test2---')
    assert 1==1

if __name__ == '__main__':
    pytest.main(['-s','test_0_1.py'])

3、执行结果:

4、示例代码:(通过参数‘--count=5’来执行,会对该测试文件里的所有测试用例依次都执行5次

import pytest

def test_01():
    print('---test1---')

# @pytest.mark.ios
# @pytest.mark.repeat(5)
def test_02():
    print('---test2---')
    assert 1==1

if __name__ == '__main__':
    pytest.main(['-s', 'test_0_1.py', '--count=5'])  # 会对该测试文件里的所有测试用例都执行5次

 5、执行结果:用例1执行完5次之后用例2再执行5

6、可以通过参数‘--repeat-scope=session’指定重复的作用范围类似fixture的scope,示例代码:

import pytest

def test_01():
    print('---test1---')

# @pytest.mark.ios
# @pytest.mark.repeat(5)
def test_02():
    print('---test2---')
    assert 1==1

if __name__ == '__main__':
    pytest.main(['-s', 'test_0_1.py', '--count=5', '--repeat-scope=session'])  # 整个文件的用例执行五次

 7、执行结果:(整个文件的用例执行五次

8、--count=10000-X参数的结合使用,在指定的重复执行次数里,知道第一次用例执行失败,就会停止继续执行;

9、--repeat-scope总结:

9.1、function:默认参数,作用范围是每个测试用例重复执行后,再执行下一个用例;

9.2、class:表示以用例集合为单位,重复执行class中的用例;

9.3、module:表示以模块为单位,重复执行模块中的用例;

9.4、session:表示以整个测试会话为单位,即所有收集的测试执行一次,然后再执行所有的用例一次;

1.6、fixture的断言机制

1.6.1、常用断言

1、常用断言:

1.1、assert XX :判断XX为真;

1.2、assert not XX:判断XX不为真;

1.3、assert a in b:判断b包含a;

1.4、assert a==b:判断a等于b;

1.5、assert a !=b:判断a不等于b;

2、示例代码:

import pytest

class Test_a():

    def test_1(self):
        assert 1

    def test_2(self):
        assert 'sh' in 'shell'

    def test_3(self):
        assert 1==1

    def test_4(self):
        assert 'shell' != 'shell_1'

if __name__ == '__main__':
    pytest.main(['-s','test_1.py'])

3、执行结果:

1.7、使用pytest.ficture装饰器携带参数

1.7.1、使用测试固件对象中的参数params

1、示例代码:

import pytest

class Calculater():
    def add(self,a,b):
        return a+b

@pytest.fixture(scope='function', params=[[1,2,3],[2,3,5]])
def get_data(request):
    # print(Calculater())
    # print(request.param)
    return Calculater(), request.param

def test_add(get_data):
    print(get_data[1][0], get_data[1][1], get_data[1][2])
    assert get_data[0].add(get_data[1][0], get_data[1][1]) == get_data[1][2]

if __name__ == '__main__':
    pytest.main(['-s', 'test_2.py'])

 2、测试结果:

3、总结:如果使用params方式实现参数化那么固件对象中传入的参数名固定是request且必须返回参数对象即request.params;

4、参数化函数也可以直接放在conftest.py里,可以将固件初始化代码和参数化代码分离,实现两个函数;

5、conftest.py文件,示例代码:

import pytest

class Calculater():
    def add(self, a, b):
        return a+b

@pytest.fixture(scope='function')
def get_data():
    return Calculater()

@pytest.fixture(params=[[1,2,3],[2,2,4]])
def get_data_params(request):
    return request.param

6、测试代码:

import pytest


def test_add_1(get_data, get_data_params):
    print(get_data_params[0],get_data_params[1],get_data_params[2])
    assert get_data.add(get_data_params[0],get_data_params[1])==get_data_params[2]

if __name__ == '__main__':
    pytest.main(['-s', 'test_3.py'])

 7、测试结果:

 8、还可以以下面形式实现,示例代码:

import pytest

class Calculater_2(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b
    def add(self):
        return self.a+self.b

@pytest.fixture(scope='function', params=[[1,1,2],[3,3,6]])
def get_data_params_2(request):
    return Calculater_2(request.param[0], request.param[1]), request.param[2]

def test_sum(get_data_params_2):
    return get_data_params_2[0].add() == get_data_params_2[1]

if __name__ == '__main__':
    pytest.main(['-s', 'test_4.py', '--disable-warnings'])

9、执行结果:

1.7.1、解析yaml文件传递数据

1、yaml文件如下:data.yaml

data:
  - [1,1,2]
  - [2,2,4]
  - [3,3,6]

2、解析yaml的文件:read_yaml.py

import yaml


def readyaml():
    with open('data.yaml', encoding='utf-8') as f:
        get_yaml = yaml.load(f, Loader=yaml.FullLoader)
    print(get_yaml)
    print(type(get_yaml))
    return get_yaml['data']

# readyaml()

3、conftest.py文件(把params=具体值,修改为从yaml解析出来的值)

import pytest
from demo10.read_yaml import *   # 导入解析yaml得到yaml值的文件
class Calculater():
    def add(self, a, b):
        return a+b

@pytest.fixture(scope='function')
def get_data():
    return Calculater()

@pytest.fixture(params=readyaml())
def get_data_params(request):
    return request.param

 4、测试文件:test_4.py

import pytest



def test_add_1(get_data, get_data_params):
    print(get_data_params[0],get_data_params[1],get_data_params[2])
    assert get_data.add(get_data_params[0],get_data_params[1])==get_data_params[2]

if __name__ == '__main__':
    pytest.main(['-s', 'test_3.py'])

5、执行test_4.py文件得到结果:

1.8、mark自定义标记

1.8.1、自定义标记测试用例并按标记执行用例

1、代码示例:(对5个测试用例做不同的标记,用例1标记为:L1;用例2和3标记为:apple;用例4和5标记为:case1)

import pytest

@pytest.mark.L1
def test_1():
    print('---test1---')

@pytest.mark.apple
def test_2():
    print('---test2---')

@pytest.mark.apple
def test_3():
    print('---test3---')

@pytest.mark.case1
def test_4():
    print('---test4---')
@pytest.mark.case1
def test_5():
    print('---test5---')

if __name__ == '__main__':
    pytest.main(['-s', 'test_5.py', '-m case1', '--disable-warnings'])

 2、执行结果:(下面结果是执行标记为:case1的)

3、如果想执行标记为case1之外的所有用例;文件中参数:

pytest.main(['-s''test_5.py''-m not case1''--disable-warnings'])

在终端中执行时命令行如下:pytest -s  test_5.py -m "not case1" 

执行结果:

​​​​​​​

3、总结直接加参数‘-m case1’或者‘-m=case1;执行所选标记以外的所有用例测试文件里添加参数时’-m not case1’,在终端中执行命令行时:-m “not case1”

你可能感兴趣的:(python,pytest测试框架,自动化测试,pytest)