一、需要安装插件
pip install pytest
pip install pytest-xdist
pip install pytest-ordering
pip install pytest-rerunfailures
pip install pytest-html
pip install allure-pytest
二、注意事项
- 文件名必须以test_开头或以_test结尾 (.py文件)
- 测试类必须以Test开头,并且不能有__init__方法(class 定义的类名)
- 测试方法必须以test开头 (def 定义的函数名)
- 默认从当前目录下收集测试用例,当配置testpath时,从配置的路径下手机测试用例
ps:修改配置文件后,模块名、测试类、测试方法应该与配置文件一致
三、使用
1、测试用例
1、创建测试
# 测试函数
def test_demo1():
print('test_01')
# 测试类
class Test_demo01():
def test_01(self):
# 正常
assert 1==1
def test_02(self):
# 错误示例
assert 1==2
2、运行测试用例
1、通过pytest命令执行测试用例
# 不会控制台打印print语句
pytest
# 打印print语句
pytest -s
# 指定文件执行
pytest -vs test_demo01.py test_demo02.py
# 指定运行目录
pytest -vs .\testdemo\ # testdemo是模块
2、通过Python代码执行(主函数)
import pytest
if __name__ == '__main__':
pytest.main(['-vs','../testdemo/test_demo01.py','../testdemo/test_demo02.py'])
3、参数详解
-s:表示输出调试信息,包括print打印的信息
import pytest
if __name__ == '__main__':
pytest.main(['-s']) #主函数模式
pytest -s #命令行模式
-v:显示更详细的信息
import pytest
if __name__ == '__main__':
pytest.main(['-v']) #主函数模式
pytest -v #命令行模式
-vs:v和s两个参数一起用
import pytest
if __name__ == '__main__':
pytest.main(['-vs']) #主函数模式
pytest -vs #命令行模式
-n:支持多线程或者分布式运行测试用例
import pytest
if __name__ == '__main__':
pytest.main(['-vs','-n=2']) #主函数模式
pytest -vs -n 2 #命令行模式
–reruns NUM:失败用例重跑,NUM为重跑次数
import pytest
if __name__ == '__main__':
pytest.main(['-vs','--reruns=2']) #主函数模式
pytest --reruns 2 #命令行模式
-x:表示只要有一个用例报错,那么测试停止
import pytest
if __name__ == '__main__':
pytest.main(['-vs','-x']) #主函数模式
pytest -x #命令行模式
–mainfaill=2 :出现两个用例失败就停止
import pytest
if __name__ == '__main__':
pytest.main(['-vs','--maxfail=2'])#主函数模式
pytest --maxfail 2 #命令行模式
-k:根据测试用例的部分字符串指定测试用例
import pytest
if __name__ == '__main__':
pytest.main(['-vs','-k=baili'])#主函数模式,若函数名包含baili则执行该用例
import pytest
if __name__ == '__main__':
pytest.main(['-vs','-k=not baili'])#主函数模式,若函数名包含baili则执行该用例
pytest -k 'baili'#命令行模式
pytest -k 'not baili'#命令行模式,不包含baili则执行
–html ./report/report.html:生成html的测试报告
在项目根目录下新建一个report包用于存储报告
import pytest
if __name__ == '__main__':
pytest.main(['-vs','--html=./report/report.html']) #主函数模式
#命令行模式
pytest --html ./report/report.html
4、执行顺序
在模块级别采用模块名的ascii码顺序,在模块内部根据从上往下的定义顺序来执行。
可使用mark标记改变pytest默认的执行顺序
class TestInterface:
def test_03_interface(self):
print('test03')
@pytest.mark.run(order=2) #定义运行顺序
def test_05_interface(self):
print('test-05')
@pytest.mark.run(order=1) #定义运行顺序
def test_06_interface(self):
print('test-06')
def test_07_interface(self):
print('test-07')
#执行顺序为6-5-3-7
3、断言
在pytest中只需使用python语言标准的断言语句 assert 来断言。
4、前后置断言
1、经典的xunit风格
xunit是一种经典且流行的方式,该方式可以基于模块/类/函数实现固定装置(设置和拆卸测试状态)
- 模块级别的setup_module和teardown_module:
在整个模块调用前后执行一次:
def setup_module():
'''
模块级前置条件
:return:
'''
print('我会在当前模块所有测试执行之前执行')
def teardown_module():
'''
模块级后置条件
:return:
'''
print('我会在当前模块所有测试执行之后执行')
def test_01():
print('sede')
class TestHaha():
def test_01(self):
print(234)
def test_02(self):
print(456)
- 类级别setup_class和teardown_class
在整个类调用前后执行一次:
class TestHaha():
@classmethod
def setup_class(cls):
'''
类级前置条件
:return:
'''
print('我会在当前类所有测试执行之前执行')
@classmethod
def teardown_class(cls):
'''
类级后置条件
:return:
'''
print('我会在当前类所有测试执行之后执行')
def test_01(self):
print(234)
def test_02(self):
print(456)
- 方法级别setup_method和teardown_method
在整个方法调用前后执行一次:
class TestHaha():
@classmethod
def setup_method(self):
'''
方法级前置条件
:return:
'''
print('我会在当前类里每个测试方法执行之前执行')
@classmethod
def teardown_method(self):
'''
方法级后置条件
:return:
'''
print('我会在当前类类每个测试方法执行之后执行')
def test_01(self):
print(234)
def test_02(self):
print(456)
- 方法级别setup_function和teardown_function
在模块每个函数调用前后执行一次:
def setup_function():
'''
函数级别的前置
:return:
'''
print('我会在当前模块在每个测试函数执行之前执行')
def teardown_function():
'''
函数级别的后置
:return:
'''
print('我会在当前模块在每个测试函数执行之后执行')
def test_01():
print('sede')
def test_02():
print('sdjf')
2、unittest风格
pytest支持unittest的夹具风格(可通过pytest的执行方式执行)
import unittest
class TestMay(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
'''
类前置条件
:return:
'''
print('我会在当前类所有测试执行之前执行')
@classmethod
def tearDownClass(cls) -> None:
'''
类后置条件
:return:
'''
print('我会在当前类所有测试执行之后执行')
def setUp(self) -> None:
'''
方法前置条件
:return:
'''
print('会在当前类里所有方法测试执行之前执行')
def tearDown(self) -> None:
'''
方法后置条件
:return:
'''
print('会在当前类里所有方法测试执行之后执行')
def test_one(self):
print('test')
self.assertEqual(1,1)
def test_two(self):
print('cde')
3、@pytest.fixture
pytest框架有一种通过装饰器实现的夹具机制
1. 定义:通过@pytest.fixture可以定义夹具
import pytest
@pytest.fixture
def fixture_func(): # 不能随便接受参数
print('pytest.fixture的一个前置条件')
yield '托尔斯泰' #yield所在行为返回值,yield上面的代码都是前置,后面的代码都是后置
print('pytest.fixture的一个后置条件')
2. 调用夹具:
- 通过装饰@pytest.mark.usefixtures(‘fixture_func’)
# 使用装饰器语法调用夹具
# 修饰函数
@pytest.mark.usefixtures('fixture_func')
def test_function():
print('我是一个测试函数')
# 修饰整个类
@pytest.mark.usefixtures('fixture_func')
class TestSome():
def test_one(self):
print('one')
def test_two(self):
print('two')
- 通过在测试函数中定义与夹具函数名同名的参数
# 使用装饰器语法调用夹具
@pytest.mark.usefixtures('fixture_func')
def test_function():
print('我是一个测试函数')
这种方式还可以接收夹具的返回值(夹具的返回值定义在yield所在行)
import pytest
@pytest.fixture
def fixture_func(): # 不能随便接受参数
print('pytest.fixture的一个前置条件')
yield 'test' #test为夹具的返回值
print('pytest.fixture的一个后置条件')
# 使用函数传参的方法来调用前置后置
def test_two(fixture_func):
print(fixture_func) #通过函数名调用夹具的返回值
print(12345)
# 在类中使用夹具返回值
class TestSome():
def test_one(self,fixture_func):
print('one')
def test_two(self):
print('two')
@pytest.fixture
def db():
import pymysql
with pymysql.connnect() as conn:
yield conn #conn为yield返回值
@pytest.mark.usefixtures("fixture_func")
def testa(db):
cursor = db.cursor() #通过函数名调用夹具的返回值
cursor.execute('select * from student')
assert cursor.fetch_one()
3.夹具的作用范围
通过@pytest.fixture装饰器的参数scope可以指定夹具的作用范围
- function 默认范围,函数范围,在测试完成后结束
- class 在类中最后一共测试完成后结束
- module 在模块中最后一个测试完成后结束
- package 在包中的最后一个测试完成后结束
- session 在一次会话中的最后一共测试完成后结束
修饰类:
import pytest
@pytest.mark.usefixtures('class_fixture')
class TestSome():
def test_one(self):
print('one')
def test_two(self):
print('two')
# 如果使用scope='class'修饰类,在整个类执行前开始,在整个类所有测试执行完之后结束
@pytest.fixture(scope='class')
def class_fixture(self):
print('我是一个类级前置')
yield
print('我是一个类级后置')
# 其他scope参数值作用范围同理
修饰模块:
# 模块级前后置,传入参数autouse=True运行时自动调用
@pytest.fixture(scope='module',autouse=True)
def moudle_fixture():
print('我是一个模块级前置')
yield
print('我是一个模块级后置')
修饰包:
在测试用例包下新建conftest.py,内容如下:
import pytest
# 设置autouse=True,当pytest执行整个测试用例包内容时,自动执行
@pytest.fixture(scope='package',autouse=True)
def package_fixture():
print('我在整个包测试开始之前执行')
yield
print('我在整个包测试执行完成之后执行')
修饰会话:
在测试用例包下新建conftest.py,内容如下:
import pytest
# 设置autouse=True,当pytest执行整个测试用例包内容时,自动执行
@pytest.fixture(scope='session',autouse=True)
def package_fixture():
print('我在整个测试执行前执行')
yield
print('我在整个测试执行后执行')
4.共享夹具
如果一个夹具需要被多个文件使用,则将其定义在conftest.py文件中,通过函数名调用,不需要在测试中倒入,pytest框架会自动发现并执行。
在同一目录下,新增conftest.py和test_01.py文件(在执行时先在当前目录下查找conftest.py中内容,若不存在则查找上层目录、上上层中conftest.py,若不存在,则报错)。
conftest.py文件内容如下:
import pytest
@pytest.fixture(scope='class')
def class_fixture():
print('我是一个类级前置')
yield
print('我是一个类级后置')
test_01.py文件内容如下:
import pytest
@pytest.mark.usefixtures('class_fixture')
class TestSome():
def test_one(self):
print('one')
def test_two(self):
print('two')
pytest运行test_01.py会自动运行执行引用的夹具
5.夹具的多层使用
import pytest
@pytest.fixture
def fixture1():
print('>1111111')
yield
print('1111111<')
@pytest.fixture
def fixture2():
print('>222222')
yield
print('222222<')
@pytest.fixture(scope='module')
def module_fixture():
print('模块前置')
yield
print('模块后置')
# 如果有多重夹具,先执行作用范围大的
# 当作用范围相同时,执行先修饰的(参数的传入顺序)
def test_func(fixture1,fixture2,module_fixture):
print('test_func')
6.夹具的继承
除了可以在测试函数中使用夹具外,夹具功能还可以使用其他的夹具。这有助于夹具的模块化设计,并允许在许多项目中重复使用特定的夹具。
注意夹具的使用只能是使用范围更广的夹具,反过来不行。模块级别的夹具不能使用类级别的夹具。
import pytest
@pytest.fixture
def fixture1():
print('》11111')
yield 1
print('11111《')
# 夹具在继承的时候,被继承的夹具范围要大于等于继承夹具
@pytest.fixture
def fixture2(fixture1): # 需要fixture1的结果
fixture1 += 1
print('》22222')
yield fixture1
print('22222《')
def test_func(fixture2):
print(fixture2)
print('test_func')
5、参数化
1. 写法一
import pytest
@pytest.fixture(params=['avs','vsd','cd3'])
def haha(request):
print('这是前置的方法')
yield request.param #return和yield都表示返回的意思,但是return的后面不能有代码,yield返回后后面可以接代码
print('这是后置的方法')
class TestInterface:
def test_03_interface(self):
print('test03')
def test_05_interface(self,haha): #调用了haha方法,会将haha中传入的每个参数执行一次,一共执行三次
print('test-05')
print(str(haha))
参数传递过程:在@pytest.fixture(params=[‘avs’,‘vsd’,‘cd3’])中进行定义,通过request传入haha,request.param获取单个参数值,参数值通过调用函数时haha传入,此时haha=单个函数值
params=[‘avs’,‘vsd’,‘cd3’]这里params是参数名,有s
request.param这里是属性名,是没有s的
注:return和yield都表示返回的意思,但是return的后面不能有代码,yield返回后后面可以接代码
2. 写法二
import pytest
test_data=[
{
'num':1,
'expect_data':1
},
{
'num':-1,
'expect_data':1
},
{
'num':0,
'expect_data':0
}
]
# 将test_data解包为多个case
@pytest.mark.parametrize('case',test_data)
def test_abs(case):
assert abs(case['num'])==case['expect_data']
import pytest
test_data2=[
[1,1],
[-1,1],
[0,0]
]
# 将test_data2解包为多个数据,然后再次解包并赋值
@pytest.mark.parametrize('num,expect',test_data2)
def test_abs2(num,expect):
assert abs(num)==expect
6、夹具别名
name:给表示的是被@pytest.fixture标记的方法取一个别名
import pytest
@pytest.fixture(name='aaa')
def haha():
print('这是前置的方法')
yield
print('这是后置的方法')
class TestInterface:
def test_03_interface(self):
print('test03')
def test_05_interface(self,aaa): #通过别名调用
print('test-05')
取了别名之后,函数原本的名称不可
四、生成报告
1、生成测试报告
pytest生成测试报告需要安装插件,请按插件说明来操作官方地址
2、生成allure报告
allure是一个专门生成测试报告的框架,支持多种语言和测试框架官方文档
3、下载、解压、配置path路径
地址
解压后运行bin目录下allure.bat文件
配置path路径:【计算机–属性–高级系统设置–环境变量–系统变量–path–编辑】(E:\test ruanjian\allure-commandline-2.14.0\allure-2.14.0\bin)
验证:打开dos窗口,输入命令allure --version
问题:dos可以验证但是pycharm验证失败,则重启pycharm
4、加入命令生产json格式的临时报告
在pytest命令中带上参数 --alluredir ,指定报告生成的路径
–alluredir ./temp
import pytest
if __name__ == '__main__':
pytest.main(['-vs','--alluredir=./temp/my_allure_results']) #主函数模式
pytest --alluredir ./temp/my_allure_results #命令行模式
#也可在配置文件中进行修改
5、生成allure报告
查看报告需要通过命令行启动allure服务
allure serve ./tmp/my_allure_results
或
os.system('allure generate ./temp/my_allure_results -o ./report --clean')
allure generate #命令,固定的
./temp #临时的json格式报告的路径
-o #输出output
./report #生成的allure报告的路径
–clean #清空./report路径原来的报告
6、上面的几个我没尝试觉得烦这边看这个
五、pytest.ini配置文件
pytest.ini这个文件是pytest单元测试框架的核心配置文件(配置文件中,因编码格式问题,需删除注释)
1、存放位置
一般放在项目的根目录
2、 编码格式
必须是ANSI,可以使用notpad++修改编码格式,或者新建txt文本,后另存文件为ANSI编码,修改文件名称
3、作用
改变pytest默认的行为/规则
4、规则明细
不管是主函数的模式运行,命令行模式运行,都会去读取这个文件
[pytest]
addopts=-vs #命令行的参数,可以输入多个,用空格分隔,主函数和命令行模式处直接执行
testpaths=./testcase #测试用例的路径
python_files=test_*.py #配置测试搜索模块名的规则
python_classes=Test #配置测试搜索类名的规则
python_functions=test #配置测试搜索方法名的规则(函数)
markers= #标记的模块
smoke:冒烟用例
uermanage:用户管理模块
productmanage:商品管理模块
六、分组执行(冒烟、分模块执行,分接口和web执行)
1.在pytest.ini文件中配置标记的模块
2.在用例处插入标记
3.通过主函数模式或命令行模式调用mark实现分组执行
#主函数模式
import pytest
if __name__ == '__main__':
pytest.main(['-vs','-m=smoke'])
import pytest
if __name__ == '__main__':
pytest.main(['-vs','-m=smoke or product'])
#命令行模式
pytest -m 'smoke'
pytest -m 'smoke or product'
七、pytets跳过用例
1、无条件跳过
无条件跳过,定义用例
@pytest.mark.skip(reason='跳过原因,可不输入')
2、规定条件跳过
有条件跳过,定义用例
@pytest.mark.skipif(age>18,reason='跳过原因') #age>18为筛选条件,满足则跳过
3、运行
import pytest
if __name__ == '__main__':
pytest.main() #主函数
pytest #命令行