目录
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、自定义标记测试用例,并按标记执行用例
conftest.py文件:是pytest框架中非常重要的一个东西,可以实现fixture对象自对应并自动应用完成跨模版、跨文件的应用操作,从而使fixture对象的定义更加灵活、方便;
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、在一个测试工程中可以存在多个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、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、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、fixture中有一个参数scope,其可以控制fixture对象的作用范围,其值有session、module、class、function、package等
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、session和module的区别:module表示针对模块而言,每个模块中使用的是同一个固件对象,不同的模块创建的固件对象是不同的;而session表示整个会话,不同模块使用的对象也是不同的;
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、代码示例:
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、先安装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、常用断言:
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、示例代码:
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、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、代码示例:(对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”