pytest学习

发布时间 2023-08-01 18:03:11作者: 石墨方

pytest

参考

https://blog.csdn.net/stetstet/article/details/119221464

https://blog.csdn.net/lovedingd/article/details/98952868

示例

最简单的示例

类级别的示例

运行方式

main方式运行

import pytest
if __name__ == "__main__":
##		pytest.main(['-s','test_example01.py'])
		pytest.main(['-s','test_example01.py'])

文件右键运行

1

​命令行运行​

命令 说明 tag
pytest test_se.py -s 显示print内容 ==辅助==
pytest test_se.py --reruns NUM
重试运行cases。NUM=重试的次数 ==执行==
pytest -x 第x次失败,就停止测试 ==执行== ==失败==
pytest --maxfail=n 出现n个失败就终止测试 ==执行== ==失败==
pytest testing/ 指定测试目录 ==执行==
pytest -k "MyClass” 通过关键字表达式过滤执行 ==执行== ==失败==
pytest test_mod.py::test_func 通过 node id 指定测试用例(nodeid由模块文件名、分隔符、类名、方法名、参数构成) ==执行== ==失败==

常用命令参数

  • 执行当前文件夹下面的所有文件

pytest 文件名/

  • 执行具体的某一测试文件

pytest 脚本名称.py

  • -k 匹配用例名称

执行测试用例名称包含qq的所有用例

pytest -k qq test_1.py

  • -k 根据用例名称排除某些用例

pytest -s -k "not qq" test_1.py

  • -k 同时匹配不同的用例名称

pytest -s -k "wechat or webo" test_1.py

  • 运行.py模块里面的某个函数

pytest test_mod.py::test_func

  • 运行.py模块里面,测试类里面的某个方法

pytest test_mod.py::TestClass::test_method

  • -x 遇到错误时停止测试

pytest -x test_class.py

  • 当用例错误个数达到指定数量时,停止测试

pytest --maxfail=1

  • -q 简单打印,只打印测试用例的执行结果

pytest -q test_1.py

  • -s 详细打印,-v 更加详细的打印,通过的.用pass表示

pytest -s test_1.py

pytest -v test_1.py

使用要求

钩子函数(前置、后置)

不同的作用范围

  • 模块级别

在当前模块下所有用例执行前去调用登录接口

def setup_module():
	buyer_login()
	print('在当前模块下所有用例执行前去调用登录接口')
def teardown_module():
	print('在当前模块下所有用例执行后要做的后置动作')
  • 函数级别

每一条测试用例执行前后都要去执行的动作

def setup_function():
	print('每条测试用例执行前都会执行')
def teardown_function():
	print('每条测试用例执行后都会执行')
  • 类级别

默认的编写规则是类以Test开头

class TestCart:
	def setup_class(self):
		print('当前类下所有的测试用例执行前调用登录接口')
	def teardown_class(self):
		print('当前类下所有的测试用例执行后执行的动作')

参数化(数据驱动)

普通方式

import pytest
test_data = [
    [1, 4, 5],
    [2, 4, 6],
    [1, -1, 0],
    [1, 4, 9]
]

def add(a, b):
return a + b

@pytest.mark.parametrize('val_a,val_b,exp_result', test_data)
def test_add(val_a, val_b, exp_result):
assert add(val_a, val_b) == exp_result

1

笛卡尔积

import pytest

test_data1 = ['aaa', 'cc']

test_data2 = ['zzzzzzz', 'xxxx', 'd']

@pytest.mark.parametrize('val_a', test_data1)
@pytest.mark.parametrize('val_b', test_data2)
def test_add(val_a, val_b):
print (val_a,val_b)

1

conftest.py

名称固定。pytest运行时自动扫描该文件,获取配置的钩子函数

使用场景

  • 如果某个前置和后置在脚本里大量使用,则可以在conftest.py中统一定义一个fixture,来进行使用
  • 但是如果某个前置或者后置,仅仅只在当前测试用例使用,那么就可以使用之前的setup_xxx和teardown_xxx

解决入参存在中文,导致的运行标题乱码

1

创建文件 conftest.py​,复制以下代码,重新运行测试用例

# 解决中文乱码
from typing import List

def pytest_collection_modifyitems(
session: "Session", config: "Config", items: List["Item"]
) -> None:
# item表示每个测试用例,解决用例名称中文显示问题
for item in items:
item.name = item.name.encode("utf-8").decode("unicode-escape")
item._nodeid = item._nodeid.encode("utf-8").decode("unicode-escape")

1

管理fixture

  • scope表示该fixture的作用域
    session表示在本次pytest执行时,只会被执行一次 module表示在每个模块执行时,只会被执行一次 class表示在每个测试类执行时,只会被执行一次 function表示在每个测试用例执行时,都会被执行 package和session基本上差不多
  • autouse表示该fixture函数是否被自动调用,默认是False

设置为True那么会根据作用域自动完成调用 设置为False则需要手动完成调用 手动调用分为两种: 1.@pytest.mark.usefixtures('get_buyer_token') # 括号里就是fixture函数的名称 2.在测试用例函数参数中直接写上fixture函数的名称

自动调用

conftest.py中加入

@pytest.fixture(scope='session', autouse=True)
def session_start():
    print("session级别的:开始测试啦")

@pytest.fixture(scope='function', autouse=True)
def func_start():
print("function级别的:准备执行测试")

1

手动调用

  • 方法一:使用装饰器,可加载测试类上,也可加载测试方法上

conftest.py中加入

@pytest.fixture(scope='function', autouse=False)
def func_start():
    print("function级别的:准备执行测试")

测试文件中加入

import pytest

def test_1():
print('test1')

@pytest.mark.usefixtures('func_start')
def test_2():
print('test2')

def test_3():
print('test3')

1

  • 方法二:直接写入方法入参中

conftest.py中加入

@pytest.fixture(scope='function', autouse=False)
def func_start():
    print("function级别的:准备执行测试")

测试文件中加入

def test_3(func_start):
    print('test3')

1

存放数据

conftest.py中加入

@pytest.fixture(scope='function', autouse=False)
def func_start():
    return '测试数据'

测试文件中加入

def test_3(func_start):
    print('str: '+func_start)

1

yield 后置处理

conftest.py中加入

@pytest.fixture(scope='function')
def some_data():
    # 前置动作
    print('get data')
    # 返回数据
    yield 'data_id'
    # 后置动作
    print('clear data')

测试文件中加入

def test_4(some_data):
    print(f'the id of the good is : {some_data}')

1

pytest配置文件

固定名称pytest.ini,便于指定范围的用例文件执行

[pytest]
addopts = -sv
testpaths = ./
python_files = test_*.py
python_classes = Test*
python_function = test_*

创建一个py文件,加入以下代码,自动扫描路径下pytest.ini,根据pytest.ini的配置执行测试

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

多断言pytest-assume

即使第一条断言失败,也会判断之后的断言

需装插件pytest-assume

def test_1():
    string1 ='iaosdf'
    pytest.assume(len(string1)>8,f'实际是{False},预期是{False}' )
    string1 = 'iis'
    pytest.assume(len(string1)>8,f'第二次:实际是{False},预期是{False}' )
    string1 = '333333ffdfdfddfd'
    pytest.assume(len(string1)>8,f'第二次:实际是{True},预期是{True}' )

1

Allure测试报告

需装插件allure-pytest

使用

  • 在pytest.ini文件中追加参数
[pytest]
addopts = -sv --alluredir ./report/data --clean-alluredir
testpaths = ./
python_files = test_*.py
python_classes = Test*
python_function = test_*
  • 使用命令行
pytest -sv --alluredir ./report/data --clean-alluredir [pytest测试文件名.py]

会生成./report/date目录,有一堆json文件

1

  • allure命令行工具配置

pycharm中terminal进入到report同级目录执行命令

allure generate ./report/data -o ./report/html --clean

也可以集成至run.py(文件名非固定)文件中

import os
import pytest
if __name__ == '__main__':
    pytest.main()
    os.system('allure generate ./report/data -o ./report/html --clean')

需要在pycharm中打开index.html选择浏览器打开

错误信息

pytest的退出码有六种:

0:所有的测试case都运行成功

1:部分(或者全部)测试case运行结果失败

2:测试执行过程被用户中断(即跑测试case过程中,用户进行Ctrl + C命令)

3:发生了内部错误(可能是python脚本某些运行时错误)

4:pytest命令行使用错误(可能没有传递正确的命令行参数)

5:实际没有执行case(可能是执行了一个空的测试类)

遇到的问题

  1. 运行找不到测试方法