0.简介

pytest是一个用于编写和运行Python单元测试的框架。它是Python生态系统中最受欢迎的测试框架之一,具有简单易用、灵活性强的特点。

pytest具有许多特性和优势,包括:

1. 简单易用:pytest提供了一种直观且简化的方式来编写测试用例。它使用简洁的语法和自动发现机制,使得编写和运行测试变得非常容易。

2. 自动化:pytest可以自动发现测试文件和测试函数。它会在指定目录中递归搜索以"test_"开头或以"_test"结尾的文件,并执行其中的测试函数。这种自动发现机制减少了手动配置的工作,使得测试更加便捷。

3. 丰富的断言:pytest提供了丰富的内置断言函数,用于验证测试结果是否符合预期。这些断言函数可以处理各种数据类型和数据结构,包括数字、字符串、列表、字典等,使得编写断言变得简单而直观。

4. 插件系统:pytest具有可扩展的插件系统,可以通过安装第三方插件来增强其功能。这些插件可以提供额外的报告输出、测试覆盖率分析、测试并发执行等功能,满足不同项目的需求。

5. 集成性:pytest可以与其他常用工具和框架集成,例如unittest、doctest和mock等。这意味着你可以使用pytest扩展已有的测试套件,或者在已有的代码中添加新的测试。

总体而言,pytest是一个功能强大、易用且灵活的Python测试框架。它通过简化测试编写过程、提供丰富的断言和灵活的插件系统,帮助开发人员编写高质量的测试用例,以确保代码的正确性和可靠性。

 

以下是列出的pytest常用模块的整理表格:

模块名 描述
pytest-randomly 随机执行测试,发现潜在的依赖或顺序问题
pytest-cov 测试覆盖率工具,用于评估测试的质量和完整性
pytest-django 方便地测试Django应用,提供了专门的fixture和命令
pytest-bdd 使用行为驱动开发(BDD)方式编写测试,使用Gherkin语言描述场景和步骤
pytest-xdist 并行执行测试,提高测试速度和效率
pytest-mock 使用mock对象模拟测试中的依赖
pytest-html 生成HTML格式的测试报告,方便查看和分享测试结果
pytest-flask 方便地测试Flask应用,提供了专门的fixture和命令
pytest-timeout 设置测试的超时时间,防止测试卡住或运行时间过长
pytest-selenium 使用Selenium测试Web应用,提供了专门的fixture和命令
pytest-qt 测试Qt应用,提供了专门的fixture和工具
pytest-env 在pytest.ini文件中设置环境变量,方便配置测试环境
pytest-repeat 重复执行测试,检测潜在的随机或不稳定问题
pytest-instafail 实时查看测试失败信息,无需等待整个测试运行结束
pytest-order 自定义测试执行顺序,满足特定需求或依赖
pytest-subtests 使用subtests概念组织和报告测试,提高可读性和可维护性
pytest-asyncio 测试异步代码,提供了专门的fixture和工具
pytest-mypy 使用mypy检查类型注解,提高代码质量和可靠性
pytest-flake8 使用flake8检查代码风格和错误,提高可读性和规范性
pytest-xray 与Xray集成,方便管理和报告测试用例和结果
pytest-vcr 使用VCR.py记录和回放HTTP请求,方便测试外部服务
pytest-check 使用多个断言语句,不会在第一个失败时停止测试
pytest-datadir 管理测试数据文件,提供了专门的fixture和工具
pytest-pydocstyle 使用pydocstyle检查文档字符串的风格和规范,提高代码可读性
pytest-benchmark 对代码性能进行基准测试,提供了专门的fixture和工具
pytest-docker 使用Docker创建和管理测试环境,提供了专门的fixture和命令
pytest-tornado 测试Tornado应用,提供了专门的fixture和工具
pytest-freezegun 使用freezegun模拟时间,方便测试时间相关的代码

 

1.基本语法

1.pytest-html 

pip3 install pytest-html

在Shell执行pytest -h可以看到pytest的命令行参数有这10大类,共132个

  类别 中文名 包含命令行参参数数量
1 positional arguments 形参 1
2 general 通用 31
3 reporting 报告 18
4 collection 收集 15
5 test session debugging and configuration 測試session调试和配置 11
6 pytest-warnings pytest警告 1
7 logging 日志 11
8 reporting-allure allure测试报告 3
9 ini-options pytest.ini/tox.ini/setup.cfg
配置文件
37
10 environment variables 环境变量 4

 

显示print

pytest --capture=no

-v, --verbose

输出详细信息

-q, --quiet

输出简要信息

–color=color

显示颜色

--html

生成报告html路径

--self-contained-html

合并css和html样式

-s 是显示带控制台输出结果

pytest -v --html=wubantu.html --color=yes --self-contained-html wubantu.py

在乌班图crontab运行要带绝对路径

/home/yys/.local/bin/pytest -v --html=/home/yys/share/report/wubantu.html --color=yes --self-contained-html /home/yys/mobile/wubantu.py

目前测试html显示日志和终端显示日志会有冲突

-k 选中模糊匹配函数,下面例如选择代02的运行

pytest.main(['-vs', __file__, '--html=./report/tt.html', '-k=02'])

collecting ... collected 2 items / 1 deselected / 1 selected

test_04.py::test_02 setup_function
test02
PASSEDteardown_function

 

3.通过全局配置文件pytest.ini文件执行。注意:
一般放在项目的根目录下,名称必须是pytest.ini

当有中文时候编码格式为GB2312
pytest.ini文件可以改变默认的测试用例规则。
不管是命令行运行也好还是说主函数运行也好,都会加载这个配置文件。
addopts多个话用空格隔开

[pytest]
addopts =-vs -q
testpaths= ./testcases
python_files = test_*.py
python_classes = Test*
python_functions = test_*

 

1.4.跳过

四、pytest跳过测试用例。

(1)无条件跳过
 

@pytest.mark.skip(reason="无理由跳过")

(2)有条件跳过

@pytest.mark.skipif(workage<10, reason="工作经验少于18年跳过")

(3)pytest.skip

如果你在使用 skipif 装饰器时无法直接访问 self,可以写在函数内部pytest.skip

import pytest

class MyTestClass:
    @pytest.mark.parametrize("some_value", [42])
    def test_something(self, some_value):
        if some_value == 42:
            pytest.skip("Some value is 42.")
        assert some_value != 42

通过这种方式,我们可以根据 some_value 的值来决定是否跳过测试用例。你可以根据你的具体需求修改条件和操作


5.pytest框架用例运行级别

>>模块级(setup_module/teardown_module)开始于横块始末,全局的 

>>函数级(setup_function/teardown_function)只对函数用例生效(不在类中)

>>类级(setup_class/teardown_calss)只在类中前后运行一次(在类中)

>>方法级(setup_method/teardown_method)开始于方法始末(在类中)

>>类里面的(setup/teardown)运行在调用方法前后

6.Fixture前后置

@pytest.fixture()scope :作用域
function:在函数之前和之后执行class:在类之前和之后执行
package/session :在整个项目会话之前和之后执行。autouse :自动执行。默认是False.

@pytest.fixture(scope="function" ,autouse=True)
def exe_database_sql():
    print("执行sQL查询")
    yield
    print("关闭数据库连接")


6.Pytest之parametrize()实现数据驱动方法:
@pytest.mark.parametrize(args_name,args_value)args_name :参数名称,用于将参数值传递给函数
args_value :参数值:(列表和字典列表,元祖和字典元祖),有n个值那么用例执行n次。
 

date = ['23', '343', '34', '343', '34', '343', '34', '343', '34', '343', '34', '343', '34', '343', '34', '343', '34', '343', '34', '343', '34']
@pytest.mark.parametrize('test_msg', date)
def test_crmSendMsg_tup(test_msg):
    print(test_msg)
    time.sleep(1)

2.多进程

安装 pytest-xdist

pip install pytest-xdist
import pytest
from time import sleep

def test_04():
    print('3434')

def test_01():
         # 通过延时,直观比较运行耗时
    print("testcase_01.")
    sleep(2)

def test_02():

    print("testcase_02.")
    sleep(3)
def test_03():

    print("testcase_03.")
    sleep(4)


if __name__ == "__main__":
    pytest.main(["-v", '-n=4', __file__, "--html=report.html", "--color=yes"])       # 使用3个进程执行,耗时4.44s
    print('23')
    

============================= test session starts =============================
platform win32 -- Python 3.9.10, pytest-7.1.2, pluggy-1.0.0 -- C:\Users\yys53\OneDrive\python\install\python.exe
cachedir: .pytest_cache
metadata: {'Python': '3.9.10', 'Platform': 'Windows-10-10.0.22000-SP0', 'Packages': {'pytest': '7.1.2', 'py': '1.11.0', 'pluggy': '1.0.0'}, 'Plugins': {'forked': '1.4.0', 'html': '3.1.1', 'metadata': '2.0.1', 'xdist': '2.5.0'}}
rootdir: C:\Users\yys53\OneDrive\python\bestscript
plugins: forked-1.4.0, html-3.1.1, metadata-2.0.1, xdist-2.5.0
gw0 I / gw1 I / gw2 I / gw3 I
[gw0] win32 Python 3.9.10 cwd: C:\Users\yys53\OneDrive\python\bestscript
[gw1] win32 Python 3.9.10 cwd: C:\Users\yys53\OneDrive\python\bestscript
[gw2] win32 Python 3.9.10 cwd: C:\Users\yys53\OneDrive\python\bestscript
[gw3] win32 Python 3.9.10 cwd: C:\Users\yys53\OneDrive\python\bestscript
[gw0] Python 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)]
[gw1] Python 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)]
[gw2] Python 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)]
[gw3] Python 3.9.10 (tags/v3.9.10:f2f3f53, Jan 17 2022, 15:14:21) [MSC v.1929 64 bit (AMD64)]
gw0 [4] / gw1 [4] / gw2 [4] / gw3 [4]

scheduling tests via LoadScheduling

文件命名2.py::test_03 
文件命名2.py::test_04 
文件命名2.py::test_01 
文件命名2.py::test_02 
[gw0] [ 25%] PASSED 文件命名2.py::test_04 
[gw1] [ 50%] PASSED 文件命名2.py::test_01 
[gw2] [ 75%] PASSED 文件命名2.py::test_02 
[gw3] [100%] PASSED 文件命名2.py::test_03 

- generated html file: file://C:\Users\yys53\OneDrive\python\bestscript\report.html -
============================== 4 passed in 5.63s ==============================

 

 

2.pytest-parallel

  pytest-parallel使用多线程后,则最后执行时间是运行时间最长的线程的时间。

1.注意:pytest-parallel目前暂不能和allure报告使用(已解决看第二点

2.安装pytest-multithreading-allure (处理 pytest-parallel 不能和测试报告插件 allure-pytest 兼容的问题)(但是遇到问题:@pytest.mark.parametrize使用日志会混乱:未解决)

安装:

pip3 install pytest-parallel

常用参数解析:

–workers (optional)  n:多进程运行,  n是进程数。默认为1。

(注意:若为window平台,则该workers永远=1,在linux和mac下可以取不同值。)

–tests-per-worker (optional)  n:多线程运行, n是每个worker运行的最大并发线程数。默认为1(该参数不受运行平台限制)。

(如果两个参数都配置了,就是进程并行,每个进程最多n个线程,总线程数:进程数 * 线程数)

在window下的cmd命令行执行:

3.allure-pytest

allure-pytest

4.yaml接口自动化测试框架的封装

1.yaml文件如何实现接口关联封装
2.yaml文件如何实现动态参数的处理3.yaml文件如何实现文件上传
4.yaml文件如何实现如何解决断言,特别是当有参数化的时候如何断言。

5.yaml文件数据量太大怎么样?
6.接口自动化框架的扩展︰加密接口,签名接口。自定义的功能接口。
深度思考:做自动化是为了什么?投入和产出比。

如何能够做到只要自动化测试框架搭建完成之后,其他的测试不需要改人任何代码,也可以通过这框架实现自动化测试?
用途∶
1.用于做配置文件
配置全局的数据︰环境变量,数据库信息,账号信息,日志格式,日志报告名称。2用于写测试用例(接口自动化测试用例)--用于数据驱动
一、YAML简介
yaml是一种数据格式(json),支持注释,换行,多行字符串,裸字符串等。
二、YAML语法规则
1.大小写敏感(区分大小写)⒉使用缩进表示层级关系。
3.缩进不管空格的数量,只要层级的左边对齐就OK4.#表示注释

三、YAML和JSON数据结构对比JSON
1.Map对象:键值对。(字典dict )使用f括起来,如:{name:百里,age:18}⒉数组,(列表list )
使用括起来,如:[{name:百里,age:18],{name:微微,age:16]]
如︰
 

msxy:
  name: 百里
  age: 18

一行的写法︰

msxy: {name: 百里,age: 18}


⒉数组,(列表list ),用一组横行""开头。如:
 

msxy:
  -name: 百里
  -age: 18

一行的写法∶

msxy : [{name: 百里},{age: 18}]


py例子

写入

allow_unicode=True 避免中文

default_flow_style=False : 表示dump后的字典数据全部以yml格式显示,默认为为True

import yaml

with open('config.yaml', 'w', encoding='utf-8') as f:
    data = [{'msxy': [{'name': '百里'}, {'age': 18}]}]
    yaml.dump(data=data, stream=f, allow_unicode=True, default_flow_style=False)
- msxy:
  - name: 百里
  - age: 18

读取 

import yaml


with open('config.yaml', encoding='utf-8') as f:
    content = yaml.load(stream=f.read(), Loader=yaml.Loader)
    print(content)

[{'msxy': [{'name': '百里'}, {'age': 18}]}]
 

5.解决@pytest.mark.parametrize乱码问题

在pytest.ini文件中写入

[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True

控制台结果[]中的中文正常现实 

============================= test session starts =============================
collecting ... collected 7 items

UIAutomator - 副本.py::test_begin_te1[note9_5g_ip-锦瑟年华152] PASSED    [ 14%]锦瑟年华152 192.168.31.50

UIAutomator - 副本.py::test_begin_te1[note8_ip-颠覆宇宙为英语] PASSED    [ 28%]颠覆宇宙为英语 192.168.31.109

UIAutomator - 副本.py::test_begin_te1[mi8_lite_ip-搜狗不行来百度1] PASSED [ 42%]搜狗不行来百度1 192.168.31.176

UIAutomator - 副本.py::test_begin_te1[redmi9_ip-16619945346] PASSED      [ 57%]16619945346 192.168.31.179

UIAutomator - 副本.py::test_begin_te1[note10pro_ip-不吃火锅] PASSED      [ 71%]不吃火锅 192.168.31.41

UIAutomator - 副本.py::test_begin_te1[mix2s_ip-被风吹过的灼思] PASSED    [ 85%]被风吹过的灼思 192.168.31.177

UIAutomator - 副本.py::test_begin_te1[redmi8A_ip-血红色的东云缀化] PASSED [100%]血红色的东云缀化 192.168.31.192


============================== 7 passed in 0.14s ==============================

Process finished with exit code 0
 

(网上说方法二改conftest.py没成功) 

6.重试

需要安装库pytest-rerunfailures

pip3 install pytest-rerunfailures

pytest支持在测试失败时自动重试测试。这对于测试失败原因不是由测试代码本身引起的随机因素(例如网络问题或并发问题)非常有用。

要启用pytest测试重试,可以使用`--reruns`参数。例如,要将测试用例重新运行两次,可以使用以下命令运行pytest:

pytest --reruns 2 test_file.py

 

这将运行`test_file.py`中的所有测试用例,并在测试失败时重试测试用例两次。

您还可以使用`--reruns-delay`参数指定重试之间的时间间隔。例如,以下命令将测试用例重新运行两次,每次间隔5秒:

pytest --reruns 2 --reruns-delay 5 test_file.py

 

注意,重试测试用例可能会增加测试运行时间,因此建议仅在必要时使用此功能。

在 pytest 中,你可以使用 @pytest.mark.parametrize 装饰器来为测试函数添加重试功能。首先,你需要导入 pytest 模块,然后使用 @pytest.mark.parametrize 来为测试函数添加参数,其中一个参数是 pytest.mark.flaky,它允许你指定测试失败时的重试次数和重试间隔。以下是一个示例:

import pytest

@pytest.mark.parametrize("retry_count", [2])  # 2 表示重试两次
@pytest.mark.flaky(reruns=2, reruns_delay=1)  # 2 表示重试两次,1 表示重试间隔为1秒
def test_example(retry_count):
    # 这个测试函数会尝试执行两次,如果失败,会在每次重试之间等待1秒
    assert some_function_to_test() == expected_result

def some_function_to_test():
    # 这里放置你的测试代码
    pass

def expected_result():
    # 这里定义你期望的测试结果
    pass

在上面的示例中,@pytest.mark.flaky 装饰器允许测试函数 test_example 失败时重试两次,并且在每次重试之间等待1秒。你可以根据需要修改 reruns 和 `reruns_delay

 

7.pytest-repeat重复运行测试用例

pytest-repeat 是一个用于 Pytest 的插件,它允许你设置整体重复运行测试用例的次数。使用 pytest-repeat 插件可以轻松地在 Pytest 中实现整体运行次数的功能,而无需编写自定义插件或使用循环结构。

以下是 pytest-repeat 插件的基本用法:

  1. 安装插件:首先,确保已经安装了 pytest-repeat 插件。你可以使用以下命令进行安装:

    pip install pytest-repeat
  2. 整体运行次数:运行测试用例:使用 pytest 命令运行你的测试用例,并使用 --count 参数指定整体运行次数。

    pytest --count=2

    在上述示例中,测试用例将会运行两次。

  3. 指定函数运行次数:为了让测试函数多次运行,你需要在测试函数上添加 @pytest.mark.repeat(n) 装饰器,其中 n 是你想要重复运行测试的次数。例如,如果你想要运行一个测试函数三次,你可以使用 @pytest.mark.repeat(3) 装饰器:

import pytest

@pytest.mark.repeat(3)  # 这将使测试函数运行三次
def test_example():
    # 这是你的测试代码

    现在,当你运行 pytest 时,被标记为 @pytest.mark.repeat 的测试函数将会按照指定的次数重复运行。

你也可以使用其他选项来更精细地控制重复运行的行为。以下是一些常用的选项:

  • --count=N:设置整体运行次数为 N。
  • --random-order:在每次运行之前随机排序测试用例的顺序。
  • --first-fail:在某次运行中出现第一个失败后停止运行剩余的重复次数。
  • --fail-last:在所有重复运行完成后,以最后一次运行的结果作为测试结果。

请注意,pytest-repeat 插件会影响整个测试套件的运行次数,包括所有的测试文件和测试用例。使用该插件时,要确保在测试用例设计和执行上没有副作用,并且每次运行的结果是可预测和可重现的。

 

您可以使用 pytest-repeat 插件来重复运行所有或指定测试用例。例如,您可以使用 --count 参数来指定重复运行的次数。例如,要重复运行整个会话,可以在命令行中输入指令:

pytest test_repeat.py -s -v --count=3 --repeat-scope=session

 ¹。

您也可以在执行的用例上加入装饰器 @pytest.mark.repeat(count) 来重复执行单个或多个用例 ²。

希望这些信息对您有所帮助!

源: 与必应的对话, 2023/7/9
(1) pytest重复运行所有或指定测试用例(pytest-repeat插件) - 习久性成 - 博客园. https://bing.com/search?q=pytest+运行2次方法.
(2) pytest---重复执行用例(pytest-repeat) - 测试-安静 - 博客园. https://www.cnblogs.com/qican/p/15038067.html.
(3) 【pytest】2.运行方法&参数 - --D - 博客园. https://www.cnblogs.com/mind18/p/15621182.html.
(4) pytest重复运行所有或指定测试用例(pytest-repeat插件) - 习久性成 - 博客园. https://www.cnblogs.com/hls-code/p/15041061.html.

8.命令行传入的参数值

您可以在 conftest.py 文件中使用 pytest_addoption 函数来添加命令行选项,然后在测试用例中使用 request.config.getoption 函数来获取命令行传入的参数值。例如,您可以在 conftest.py 文件中添加以下代码来定义一个名为 --file_name 的命令行选项:

 

conftest.py 


def pytest_addoption(parser):
    parser.addoption(
        "--paramet",
        action="store"
    )

@pytest.fixture(scope='session', autouse=False)
def paramet(request):
    paramet = request.config.getoption("--paramet")
    if '[' in paramet and ']' in paramet:
        # 字符串转列表
        import ast
        paramet = ast.literal_eval(paramet)
        
    return paramet
pytest --paramet=1

 

9.pytest-timeout

pytest-timeout 是一个用于设置测试超时的 pytest 插件。超时总是以秒为单位指定,可以通过多种方式定义,优先级从低到高如下:

  1. 您可以在 pytest 配置文件中使用 timeout 选项设置全局超时。例如:[pytest] timeout = 300
  2. PYTEST_TIMEOUT 环境变量设置全局超时,覆盖配置文件中可能的值。
  3. --timeout 命令行选项设置全局超时,覆盖环境变量和配置选项。
  4. 使用测试项上的 timeout 标记,您可以逐项指定超时:@pytest.mark.timeout(300) def test_foo(): pass
    将超时设置为 0 秒将禁用超时,因此如果您已设置全局超时,则仍然可以使用标记禁用超时¹。希望这些信息对您有所帮助!