自动化测试之Pytest框架

1 介绍

pytest 是一个功能强大且易于使用的Python测试框架,它允许开发者编写简单或复杂的函数式测试;pytest 的设计理念是让测试过程尽可能的简单和直观,同时提供丰富的插件生态系统来扩展其功能。

介绍:

  • 简单易用:pytest的语法非常简洁,我们不需要通过大量的配置和其他方式就能直接启动pytest完成测试
  • 自动发现与自动测试 : pytest 能够自动发现项目中的测试文件和测试函数,无需手动编写繁琐的配置
  • 丰富断言: pytest 内置了丰富的断言库,可以轻松地进行测试结果的判断
  • 支持参数化测试: pytest 支持参数化测试,能够快速地对多组输入进行测试
  • 插件与第三方库丰富: pytest 有着丰富的插件生态系统,可以通过插件扩展各种功能,比如覆盖率测试、测试报告生成等

2 安装

2.1 命令行安装

我们可以在pycharm中手动安装,也可以在cmd文件中直接命令安装

python 复制代码
pip install pytest

2.2 验证安装

python 复制代码
pytest --version

2.3 插件安装

python 复制代码
# 升级pytest
pip install -U pytest

# 查看pytest版本
pytest --version

# 查看已安装包列表
pip list

# 查看pytest帮助文档
pytest -h

# 安装第三方插件
pip install pytest-sugar
pip install pytest-rerunfailures
pip install pytest-xdist
pip install pytest-assume
pip install pytest-html

插件介绍:

  • pytest-sugar :这个插件为Pytest的测试用例添加了进度条,让测试过程更加可视化
  • pytest-rerunfailures :这个插件通过重跑机制来消除不稳定失败的测试用例;如果某个测试用例失败了,它会尝试重新运行该用例,直到该用例稳定通过或者达到最大重试次数
  • pytest-xdist :这是一个分布式执行插件,能够实现多个CPU或者主机执行,动态决定测试用例的执行顺序;使用这个插件可以加快测试的执行速度,因为它能够同时运行多个测试用例
  • pytest-assume :这个插件支持在测试用例中添加假设断言,类似于其他语言的 assume 函数;如果假设断言失败了,测试用例会立即跳过而不报告失败
  • pytest-html :这个插件可以生成HTML格式的测试报告;它支持多种HTML测试报告生成方式,并可以与 pytest-xdist 等插件结合使用

3 Pytest设计测试用例注意点

3.1 pytest的基本组成

pytest由两部分组成:

  • 用例主体部分(通常单独放在一个py文件):主体部分写测试用例
  • 用例运行语句(通常放在一个main文件): 执行测试用例

这里我们需要注意,对一个pytest而言,我们这块的内容非常明确,用例的运行需要通过启动的方式来解决问题

3.2 命名规范

文件名:文件名要以test_开头或结尾 例如:test_login.py

函数名:函数名要以test_开头,这样子有助与python自动去搜索

类名:如果使用类来组织测试,类名应以 Test 开头,并且不应继承自任何特定的基类(除非是为了使用某些特性)

3.3 断言清晰

文件名:文件名要以test_开头或结尾 例如:test_login.py

函数名:函数名要以test_开头,这样子有助与python自动去搜索

类名:如果使用类来组织测试,类名应以 Test 开头,并且不应继承自任何特定的基类(除非是为了使用某些特性)

3.4 fixture

fixture是pytest中非常重要的概念,用于设置测试环境,我们要合理的使用fixture,减少代码的重复使用,提高测试效率

3.5 参数化设置

利用 @pytest.mark.parametrize 装饰器可以为同一个测试函数提供多组输入数据,从而避免编写多个类似的测试函数

3.6 测试隔离

使用setup和teardowm来准备测试环境

3.7 异常处理

如果你的测试预期某个操作会抛出异常,可以使用 pytest.raises 上下文管理器来检查是否确实发生了预期的异常

3.8 跳过或者预期失败

对于暂时无法通过的测试,可以使用 @pytest.mark.skip 或 @pytest.mark.xfail 标记,以便在不影响整体测试结果的情况下继续开发

3.9 mocking

当测试需要依赖外部系统(如数据库、网络服务等)时,考虑使用 unittest.mock 或者第三方库如 pytest-mock 来模拟这些依赖,确保测试的快速性和稳定性

3.10 标记测试

使用 @pytest.mark 可以为测试添加标签,比如 slow, network, database 等,然后可以根据这些标签选择性地运行测试

4 使用Pytest书写简单案例

4.1 第一个pytest测试案例

创建一个名为Test_demo的文件名,其中包含三个函数,一个计算函数,两个测试函数

python 复制代码
import pytest

def func(x):
    return x + 1

def test_answer1():
    assert func(3) == 5  # 断言

def test_answer2():
    assert func(3) == 4  # 断言

# 测试入口:用例执行
if __name__ == '__main__':
    pytest.main()

4.2 一个类中存在多个测试分组

创建一个名为Test_demo1的文件名,新建一个类,类中包含两个测试函数

python 复制代码
import pytest
class TestClassDemoInstance:
    value = 0

    def test_one(self):
        self.value = 1
        assert self.value == 1

    def test_two(self):
        assert self.value == 1

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

4.3 Pytest运行命令可选参数

pytest命令行选项分类:

  • 环境配置选项:这些选项用于设置 pytest 的环境配置,例如设置日志级别、覆盖配置文件、设置测试模式等。
  • 测试过滤选项:这些选项用于过滤和选择测试用例,例如指定测试目录、选择特定测试模块、运行指定测试函数等。
  • 测试执行选项:这些选项用于控制 pytest 的测试执行过程,例如重试失败测试、生成测试报告、并行执行测试等。
4.3.1 环境配置选项

|------------------|---------------------------------------|------------------|
| 命令 | 说明 | 示例 |
| --version | 打印 pytest 版本号 | pytest --version |
| -h、--help | 显示 pytest 命令行选项和使用说明 | pytest --help |
| --verbose, -v | 增加测试结果输出的详细程度 | pytest -v |
| --quiet, -q | 减少测试结果输出的详细程度 | |
| --traceconfig | 显示解析和加载配置文件时的详细信息 | |
| --pdb | 当测试失败或者发生异常时,进入 Python 调试器 | |
| --pdbcls | 自定义调试器的类型 | |
| --capture | 设置 pytest 的标准输出流捕获方式,有三个值可选:sys、fd、no | |
| --norecursedirs | 设定哪些目录不搜索测试用例 | |
| --rootdir | 设置 pytest 的根目录 | |
| --maxfail=n | 设定在第 n 次测试失败后停止测试执行 | |
| --junit-xml=PATH | 将测试结果输出到 JUnit XML 文件中 | |

4.3.2 测试过滤选项

|---------------------------|----------------------------------|-----------------------------|
| 命令 | 说明 | 示例 |
| path | 指定测试目录或文件,可以是相对路径或绝对路径 | pytest tests/ |
| -m | 选择特定标记的测试用例进行执行 | pytest -m "slow" |
| -k | 选择包含某个关键字的测试用例进行执行 | pytest -k "add or subtract" |
| -x | 遇到一条测试用例失败就停止测试执行 | |
| --sw | 重跑上次修改过的测试模块 | |
| --last-failed-no-failures | 只重跑上次失败的测试用例(如果没有失败的测试用例,则不执行测试) | |
| --collect-only | 只执行测试用例的收集阶段,不运行测试用例的执行阶段 | |
| --count | 运行指定数量的测试用例 | pytest -v --count=10 |
| --trace | 显示 pytest 的内部跟踪信息 | |

使用情况:

(1)-m:选择特定标记的测试用例进行执行

该选项可以通过在测试用例中使用 @pytest.mark 标记来进行使用,示例:

python 复制代码
import pytest
@pytest.mark.slow
def test_slow():
    print("this is slow")
def test_not_slow():
    print("this is not slow")

# 执行入口-命令行方式:pytest -v -m "slow" 99_test\Test_demo3.py

执行命令后只运行标有 @pytest.mark.slow 标记的测试用例函数,如图:

(2)-k:选择包含某个关键字的测试用例进行执行

该选项可以通过执行 pytest -k "add or sub" 命令来使用,只运行测试用例名称中包含

"add" 或者 "sub" 字符串的测试用例,示例:

python 复制代码
import pytest

def test_add():
    print("this is add")

def test_sub():
    print("this is sub")

def test_mul():
    print("this is mul")

# 执行入口-命令行方式:pytest -v -k "add or sub" 99_test\Test_demo3.py

结果如下:

4.3.3 测试执行选项

|---------------------------------|---------------------------------------|-------------|
| 命令 | 说明 | 示例 |
| -n | 并行运行测试用例,可以在后面跟一个数字指定并发度 | pytest -n 4 |
| x | 遇到一条测试用例失败就停止测试执行 | |
| --maxfail=n | 设定在第 n 次测试失败后停止测试执行 | |
| --lf 、--last-failed | 只重跑上次测试失败的测试用例 | |
| --ff 、--failed-first | 只重跑上次测试失败的测试用例,并在全部测试结束后再重新运行一遍这些测试用例 | |
| --reruns=n | 在测试用例失败的情况下,重新运行 n 次测试 | |
| --pdbcls | 自定义调试器的类型 | |
| --junit-xml=PATH | 将测试结果输出到 JUnit XML 文件中 | |
| --html=PATH | 将测试结果输出到 HTML 文件中 | |
| --tb=long/short/line/native/no | 设置输出错误信息的格式 | |
| --capture=no | 禁止捕获标准输出和标准错误,直接将它们输出到终端 | |
| --capture=sys/stdout/stderr | 设置 pytest 的标准输出流捕获方式,有三个值可选:sys、fd、no | |
| -s、--show-capture=all/failed/no | 控制是否显示捕获的标准输出流 | |
| --disable-warnings | 禁用 pytest 的警告信息 | |

5 Pytest的前置与后置处理

在Pytest中,前置处理(setup)和后置处理(teardown)是非常重要的概念,它们用于在每个测试用例执行前后进行一些准备工作和清理工作。Pytest提供了多种方式来处理前置和后置操作,使得测试代码更加灵活和可维护。

5.1 前置处理

前置处理是在测试用例执行之前执行的一些操作,通常用于准备测试环境,例如创建数据库连

接、初始化测试数据、打开文件等

5.1.1 setup_module

在一个模块(.py文件)中的所有测试用例执行之前只执行一次

5.1.2 setup_function

在每个测试用例执行之前都会执行

5.1.3 setup_class

在一个类中的所有测试用例执行之前只执行一次(需要配合 @pytest.mark.usefixtures 使用或者使用类方法)

5.1.4 setup_method

在一个类中的每个测试用例执行之前都会执行(需要配合类方法使用)

5.2 后置处理

后置处理是在测试用例执行之后执行的一些操作,通常用于清理测试环境,例如关闭数据库连接、删除测试数据、关闭文件等

5.2.1 teardown_module

在一个模块(.py文件)中的所有测试用例执行之后只执行一次

5.2.2 teardown_function

在每个测试用例执行之后都会执行

5.2.3 teardown_class

在一个类中的所有测试用例执行之后只执行一次(需要配合类方法使用)

5.2.4 teardown_method

在一个类中的每个测试用例执行之后都会执行(需要配合类方法使用)

5.3 参考案例及运行结果

代码如下:

python 复制代码
# 镜像:https://pypi.tuna.tsinghua.edu.cn/simple/
import pytest

"""
pytest的前置处理与后置处理
"""

def setup_module():
    print("模块级别的前置处理")


def teardown_module():
    print("模块级别的后置处理")


def setup_function():
    print("函数级别的前置处理")


def teardown_function():
    print("函数级别的后置处理")


class TestDemo:

    # 类级别前置和后置可能不会按照预期工作,因此Pytest的类级别前置和后置处理通常需要使用@classmethod装饰器
    @classmethod
    def setup_class(self):
        print("类级别的前置处理")

    @classmethod
    def teardown_class(self):
        print("类级别的后置处理")

    def setup_method(self):
        print("类中的方法前置处理")

    def teardown_method(self):
        print("类中的方法后置处理")

    def test_03(self):
        print("类中的方法-测试用例3")

    def test_04(self):
        print("类中的方法-测试用例4")


def test_01():
    print("测试用例1")


def test_02():
    print("测试用例2")


if __name__ == '__main__':
    pytest.main(["-vs"])

执行效果如下:

6 Pytest的标签使用

在Pytest中,标签(也称为标记)是⼀种⽤于对测试⽤例进⾏分类或分组的机制。通过为测试⽤例添加标签,可以在运⾏测试时选择性地执⾏特定标签的测试⽤例,从⽽⽅便地控制测试的范围和⽬标。

Pytest的标签功能基于装饰器来实现,可以使⽤ @pytest.mark 装饰器为测试函数或测试类添加标签。常⻅的⽤法是在测试函数或测试类上⽅添加装饰器,并指定相应的标签。

6.1 使用pytest标签示例

步骤:新建一个项目包,项目内容如下:

【test_001.py】

python 复制代码
【test_001.py】

import pytest


@pytest.mark.smoke
def test_login():
    # 测试登录功能
    assert True


@pytest.mark.regression
def test_registration():
    # 测试注册功能
    assert True


@pytest.mark.smoke
def test_add_to_cart():
    # 测试添加到购物车功能
    assert True


@pytest.mark.regression
def test_checkout():
    # 测试结账功能
    assert True


@pytest.mark.skip
def test001():
    print("001")


@pytest.mark.smoke
@pytest.mark.yl
def test_test_002():
    print("添加一个标签后然后运行")

main.py

如果要运行特定标签的测试用例,可以使用 -m 选项并指定标签名称,例如,要运行标记为"smoke"或者标记为"yl"的测试用例,可以使用如下命令:

python 复制代码
【main.py】

pytest -m "smoke or yl"
pytest -m "yl"
文件内容如下:

import pytest

"""用例运行语句"""
# 只执行标签为smoke的用例
# pytest.main(["-vs","-m","smoke"])

# 标签【@pytest.mark.skip】表示跳过该条用例
# pytest.main(["-vs"])
# pytest自定义标签:@pytest.mark.标记名   注意:我们自己定义的标签需要在ini文件中声明(不声明的话,执行时会有警告)
# pytest.main(["-vs","-m","yl"])


"""
标签支持逻辑筛选
"""
# pytest.main(["-vs", "-m", "yl"])  # 是
# pytest.main(["-m", "not yl", "-vs"])  # 非

# pytest.main(["-vs", "-m", "yl or smoke"])  # 或
pytest.main(["-vs", "-m", "yl and smoke"])  # 和

【pytest.ini】

在项目根目录下的 pytest.ini 文件中可以指定全局标记,以应用至整个项目

测试案例中的 @pytest.mark.smoke 和 @pytest.mark.regression和 @pytest.mark.yl 是三个自定义的标签,因此需要在该文件中声明,否则执行时会弹警告

python 复制代码
【pytest.ini】

[pytest]
markers =
    smoke: Run smoke tests
    regression: Run regression tests
    yl: Run yl tests

6.2 pytest中的常用标签

6.2.1 skip跳转测试用例

(1)用法

python 复制代码
@pytest.mark.skip pytest.mark.skip(*, reason=None)
@pytest.mark.skipif pytest.mark.skipif(condition, *, reason=None)

(2)使用场景

  • 调试时不想运行这个用例
  • 标记无法在某些平台上运行的测试功能
  • 在某些版本中执行,其他版本中跳过
  • 当前外部资源不可用时跳过(如果测试数据是从数据库中获取的,连接数据库未成功则跳过-因为执行会报错)

(3)代码示例

python 复制代码
import time
import pytest

a = 4
b = 3
flag = True

@pytest.mark.skip(reason="直接跳过执行")
def test_case1():
    assert 1 == 2

# condition 条件:只认 False 或者 True ,如果要简单操作,则直接采用 True False 控制
@pytest.mark.skipif(condition= a > b ,reason="当a > b时,该用例跳过执行")
def test_case2():
    assert 1 == 2

# @pytest.mark.skipif(condition= a < b ,reason="当a < b时,该用例跳过执行")
# def test_case3():
#     assert 1 == 2


# 如果某些代码执行比较慢,但是我们又发现有bug,正好开发在修复,为了节约时间,在开发修复完毕前,可以直接跳过
@pytest.mark.skipif(condition= flag ,reason="使用变量flag = True,使得该用例跳过执行")
def test_case4():
    time.sleep(5)
    assert 1 == 2

if __name__ == '__main__':
    pytest.main(["-vs"])

6.2.2 xfail标记用例为失败

(1)用法

python 复制代码
pytest.mark.xfail(condition=None, *, reason=None, raises=None, run=True, strict=False)

(2)使用场景

  • 功能测试尚未实施或尚未修复的错误,当测试通过时尽管预计会失败(标记为pytest.mark.xfail),它是一个xpass,将在测试摘要中报告
  • 你希望测试由于某种情况而就应该失败

(3)代码示例

python 复制代码
import pytest


def test_case1():
    assert 2 == 2

# 使用xfail标记用例为失败后,无论用例执行结果如何,都展示为标记失败
@pytest.mark.xfail(reason="xfail标记该用例为失败")
def test_case2():
    assert 1 == 2

def test_case3():
    assert 2 == 2

if __name__ == '__main__':
    pytest.main(["-vs"])

6.2.3 自定义标记mark只执行某部分用例

(1)用法

python 复制代码
在测试用例方法上加@pytest.mark.标记名
@pytest.mark.标记名

(2)使用场景

  • 只执行符合要求的某一部分用例,可以把一个web项目划分多个模块,然后指定模块名称执行
  • APP自动化时,如果想Android和IOS公用同一套代码时,也可以使用标记功能,标明哪些是IOS的用例,哪些是Android的,运行代码时指定mark名称运行就可以

(3)代码示例

python 复制代码
import pytest

"""
使用自定义标记mark只执行某部分用例
1、场景:
(1)只执行符合要求的某一部分用例,可以把一个web项目划分多个模块,然后指定模块名称执行
(2)APP自动化时,如果想Android和IOS公用同一套代码时,也可以使用标记功能,标明哪些是IOS的用例,哪些是Android的,运行代码时指定mark名称运行就可以
2、解决:在测试用例方法上加@pytest.mark.标记名
3、执行:
(1)-s:用例方法上加@pytest.mark.标记名输出所有测试用的print信息
(2)-m:执行自定义标记相关用例,如pytest -s -m 自定义标记名称
示例:
    pytest -vs -m login Test_mark.py
    pytest -vs -m search Test_mark.py
    pytest -vs -m "search or login" Test_mark.py
"""


@pytest.mark.login
def test_login():
    print("登录用例")


@pytest.mark.search
def test_search1():
    print("搜索用例1")


@pytest.mark.search
def test_search2():
    print("搜索用例2")

if __name__ == '__main__':
    pytest.main(["-vs"])

7 Pytest的conftest使用

当使⽤ Pytest 进⾏测试时, conftest.py 是⼀个特殊的⽂件,⽤于管理测试⽤例中需要通⽤数据传递

示例(新建软件包):

conftest.py

python 复制代码
【conftest.py】

import pytest

@pytest.fixture
def setup_data1():
    return [1,2,3,4,5]

# 指定 fixture 的作用域为测试模块
@pytest.fixture(scope="function")
def setup_module():
    print("Function setup")
    yield
    print("Function teardown")


# 参数化 fixture
@pytest.fixture(params=[1, 2, 3])
def setup_data2(request):
    print(f"Setup data2: {request.param}")
    return request.param


# 自动使用 fixture
@pytest.fixture(autouse=True)
def setup_autouse():
    print("Autouse fixture")


# 自定义 fixture 名称
@pytest.fixture(name="custom_fixture")
def setup_custom():
    print("Custom fixture")

pytest.fixture 是 Pytest 测试框架中的⼀个装饰器,用于定义测试用例中需要共享的资源、数据或设置的函数。它可以在测试用例执行之前、之后或在每个测试用例之前、之后运行,并提供⼀种方便的方式来管理测试用例的前置条件和后置操作。

使用 pytest.fixture 装饰器,你可以创建⼀个被 Pytest 自动调用的函数,该函数可以为测试⽤例提供所需的初始化步骤或共享的数据。这个函数可以返回⼀个值,该值将作为参数传递给测试用例函数。

通过使用 pytest.fixture ,你可以更方便地管理测试用例的前置条件和后置操作,实现测试数据的共享和代码的重用,从而使测试代码更加简洁、可维护和可扩展。

pytest.fixture 装饰器可以接受⼀些参数,用于配置和定制 fixture 函数的⾏为

以下是⼀些常用的参数:

(1) scope :指定 fixture 的作用域。可以设置为以下几个值之⼀:

  • "function" (默认值):每个测试函数都会调用⼀次 fixture
  • "class" :每个测试类都会调用⼀次 fixture
  • "module" :每个测试模块都会调用⼀次 fixture
  • "session" :整个测试会话只会调用⼀次 fixture

(2)params :参数化 fixture,可以根据不同的参数值生成多个独⽴的 fixture 实例,可以传递⼀个可迭代对象,每个元素都会作为参数值调用 fixture。

(3)autouse :自动使用 fixture,无需在测试函数中显式声明使用该 fixture。可以设置为 True 或 False 。

(4)name :为 fixture 指定⼀个自定义名称,用于在测试报告和日志中标识 fixture。

(5)其他参数:可以根据需要添加其他自定义参数,供 fixture 函数使用。

main.py

python 复制代码
【main.py】

import pytest

pytest.main(["-vs"])

【test_001.py】

python 复制代码
【test_001.py】

def test_sum(setup_data1):
    print(setup_data1)
    assert sum(setup_data1) == 15

def test_traversal(setup_data1):
    print(setup_data1)
    for i in setup_data1:
        assert i in setup_data1

【test_002.py】

python 复制代码
【test_002.py】

# 测试函数使用 fixture
def test_example(setup_module, setup_data2, custom_fixture):
    print(f"Test example: {setup_data2}")

8 Pytest的ini文件的使用

Pytest ⽀持使⽤ pytest.ini ⽂件来配置测试运⾏的各种选项和参数。 pytest.ini ⽂件位于测试项⽬的根⽬录下,可以通过在该⽂件中设置不同的选项来⾃定义测试的⾏为。下⾯是⼀些 pytest.ini ⽂件的使⽤示例和说明。

8.1 配置默认的命令行选项

python 复制代码
[pytest]
addopts = -v -s

上述配置使用 addopts 选项指定了默认的命令行选项, -v 表示启用详细输出,-s 表示禁止捕获标准输出

8.2 选择要运行的测试文件或目录

python 复制代码
[pytest]
testpaths = tests/

上述配置使用 testpaths 选项指定了要运行的测试文件或目录的路径

8.3 自定义夹具搜索规则

python 复制代码
[pytest]
python_files = test_*.py
python_classes = Test*
python_functions = test_*

上述配置使⽤ python_files 、 python_classes 和 python_functions 选项指定了⾃定义夹具的搜索规则,分别指定了测试⽂件、测试类和测试函数的名称模式。

8.4 禁用某些插件

python 复制代码
[pytest]
addopts = --disable-warnings

上述配置使用 addopts 选项指定了禁用某些插件的选项, --disable-warnings 表示禁⽤警告信息

注意:pytest.ini文件中的选项和参数设置将适用于整个测试项目,但也可以在命令行中使用选项覆盖部分配置

9 Pytest-html的使用

9.1 pytest-html生成报告

Pytest-HTML 是⼀个插件,它可以⽣成漂亮且易于阅读的 HTML 测试报告,下⾯是使用 pytest-html 生成报告的步骤:

9.1.1 安装 pytest-html 插件
python 复制代码
pip install pytest-html
9.1.2 运行测试并生成报告
python 复制代码
import pytest

# pytest.main(["--html=report/report.html"])
pytest.main(["--html=report/report.html", "--self-contained-html"])

main.py】文件中,使用 --html 选项指定报告文件的名称和路径

  • --html=report/report.html:指定在main文件同级目录下新建report文件夹,并在该文件夹下新建report报告(默认html报告中的样式文件拆分生成)
  • --self-contained-html:在生成html文件时,将样式.css文件直接嵌入html文件中
9.1.3 查看生成的报告

打开生成的html报告文件,可以看到测试结果的摘要、详细的测试用例执行信息、失败用例跟踪等,报告通常包括以下内容:

  • 概述信息:显示运行的测试数量、通过的测试数量、失败的测试数量等概览信息
  • 测试用例列表:列出每个测试用例的名称、状态(通过、失败、跳过等)、执行时间等信息
  • 错误和失败详情:提供失败用例的详细信息、堆栈跟踪等,帮助我们快速定位和解决问题
  • 图表和图形化统计信息:可视化展示测试结果、用例通过率、执行时间等数据

示例:

9.1.4 自定义报告的样式和配置

pytest-html 提供了许多配置选项,我们可以在 pytest.ini文件中添加如下配置来自定义报告的样式和行为

python 复制代码
[pytest] [pytest]
addopts = --html=report.html --self-contained-html
html_title = My Test Report

9.2 pytest-html在报告中添加图片

在pytest中,可以使用 extra 参数和 extras 参数来向 pytest-html 报告添加自定义的额外信息,包括图片

python 复制代码
from pytest_html import extras


def test_add_img(extra):  # extra:pytest的默认的外部数据列表
    def image_to_base64(image_path):
        import base64
        """这个函数是将图片转化为base64数据"""
        with open(image_path, "rb") as image_file:
            encoded_string = base64.b64encode(image_file.read())
            return encoded_string.decode('utf-8')

    # 添加图片文件
    image_path = "./img/22.jpg"  # 自己的图片路径
    # extra.append(extras.jpg(image_path))  # 添加图片文件,无法解析,需要转为base64格式

    # 添加base64格式的图片
    base64_data = image_to_base64(image_path)
    extra.append(extras.image(base64_data))

9.3 Pytest获取用例结果流程

pytest_runtest_makereport 是 pytest 的钩子函数之一,它在每个测试用例运行完成后被调用,并提供了测试结果的详细信息;

通过使用这个钩子函数,可以自定义测试报告的输出,包括添加截图、日志等附加信息。

示例:

该函数放在 conftest.py 文件中,每条用例被执行完后,获取该条用例的执行结果(成功或者失败)并返回,文件代码如下:

python 复制代码
import pytest

@pytest.hookimpl(hookwrapper=True)  # 写死的
def pytest_runtest_makereport(item, call):  # 写死的
    outcome = yield
    report = outcome.get_result()
    if report.when == "call":  # 过滤前置,和后置,只保留运行中的状态
        print("用例执行结果:", report.outcome)  # 用例执行结果

9.4 pytest-html的异常时添加图片实现

该函数放在 conftest.py 文件中,每条用例被执行完后,获取该条用例的执行结果,判断是否为passed,若不是,则插入对应的图片

python 复制代码
import pytest
from pytest_html import extras


@pytest.hookimpl(hookwrapper=True)  # 写死的
def pytest_runtest_makereport(item, call):  # 写死的
    def image_to_base64(image_path):
        import base64
        """这个函数是将图片转化为base64数据"""
        with open(image_path, "rb") as image_file:
            encoded_string = base64.b64encode(image_file.read())
            return encoded_string.decode('utf-8')

    outcome = yield
    report = outcome.get_result()
    extra = getattr(report, "extra", [])
    if report.when == "call":  # 过滤 前置,和后置,只保留运行中的状态
        print("用例执行结果:", report.outcome)  # 用例执行结果
        if report.outcome != "passed":
            """失败截图数据"""
            image_path = "./img/22.jpg"  # 这里你可以换成你的图片路径
            base64_data = image_to_base64(image_path)
            extra.append(extras.image(base64_data))
    report.extra = extra  # 替换数据中的 extra 对象

10 Allure-pytest的使用

10.1 allure-pytest 生成测试报告

allure 生成测试报告首先需要先下载allure命令行工具

下载地址为:Central Repository: io/qameta/allure/allure-commandline

注意:任意版本都可,我这里使⽤的是 allure==2.32.0 的版本

10.1.1 下载 allure-pytest 插件
python 复制代码
pip install allure-pytest
10.1.2 allure 生成测试报告

allure 生成测试报告有两个环节:得到测试数据、根据结果数据生成测试报告

python 复制代码
import pytest

pytest.main(["--alluredir", "allure_result", "--clean-alluredir"])  # 获得测试结果,并以allure的数据格式保存下来

import os

os.system("allure generate --clean ./allure_result -o ./allure_report")  # 通过allure的数据进行报告的生成

(1)得到测试数据

获取测试结果,并以allure的数据格式保存下来,字段含义:

--alluredir:用于指定生成 allure 报告所需的数据文件的存储目录

--allure_result:这是--alluredir后面的参数,指定了存储数据的路径

--clean-alluredir:清除生成的数据

(2)根据结果数据生成测试报告

allure generate:这是 allure 命令行工具的命令,用于根据测试结果数据生成测试报告

--clean:这是一个选项,用于在生成报告之前先清理输出目录,以确保报告是最新的

./allure_result:这是存储测试结果数据的路径

-o ./allure_report:这是指定生成的 allure 报告的输出路径

10.2 allure-pytest的行为驱动标记

在 Allure 报告中, feature 和 story 被称为行为驱动标记,用于描述测试用例所属的功能和故事。

  • feature 标记用于标识测试用例所属的功能或模块。它表示被测试的系统中的⼀个主要功能。可以将 feature 视为⼀个⼤的分类或主题,用于组织和描述相关的测试用例。
  • story 标记用于进⼀步细分 feature ,描述测试用例所属的具体故事或场景。它表示在功能中的⼀个具体情境或使用案例。可以将 story 视为 feature 的子分类,用于更详细地描述测试用例。

这些行为驱动标记可以通过在测试用例的装饰器中添加相应的注解来指定。例如,在使用 pytest运行测试用例时,可以在测试函数上使用装饰器来添加 feature 和story 标记,如下所示:

python 复制代码
【test_001.py】

import allure


@allure.feature("用户管理")
@allure.story("创建用户")
def test_001():
    print("用例test_001被运行")


@allure.feature("用户管理")
@allure.story("创建用户1")
def test_002():
    print("用例test_002被运行")


def test_003():
    print("用例test_003被运行")



【test_002.py】

import allure


@allure.feature("用户管理22")
class Test:
    @allure.story("用例4")
    def test_004(self):
        print("用例test_001被运行")

    @allure.story("用例5")
    def test_005(self):
        print("用例test_002被运行")

    @allure.story("用例6")
    def test_006(self):
        print("这是一条异常用例")
        a = 1
        b = 2
        assert a + b == 4

10.3 allure-pytest的步骤管理

Allure-pytest 提供了步骤管理功能,可以帮助在测试用例中记录和展示测试步骤的执行情况。步骤管理可以提供更详细的测试过程描述,帮助定位问题和跟踪测试执行。下面是使用 Allure-pytest 进行步骤管理的简单步骤:

(1)在测试用例中使用 @allure.step 装饰器定义步骤

python 复制代码
import allure


@allure.step("步骤1:登录系统")
def step_login_system(username, password):
    # 登录系统的代码逻辑
    print("登录", username, password)


@allure.step("步骤2:搜索商品")
def step_search_product(product_name):
    # 搜索商品的代码逻辑
    print("搜索商品", product_name)


@allure.step("步骤3:添加商品到购物车")
def step_add_to_cart(product_id):
    # 添加商品到购物车的代码逻辑
    print("添加商品id:", product_id, "到购物车")


@allure.step("步骤4:结算购物车")
def step_checkout_cart():
    # 结算购物车的代码逻辑
    print("结算购物车")


@allure.step("步骤5:确认订单")
def step_confirm_order():
    # 确认订单的代码逻辑
    print("确认订单")


def test_shopping_flow():
    step_login_system("testuser", "password")
    step_search_product("iPhone")
    step_add_to_cart("12345")
    step_checkout_cart()
    step_confirm_order()
    assert True

(2)在测试用例中按照顺序调用定义的步骤

python 复制代码
import allure


def test_step_show():
    with allure.step("步骤1"):
        print("步骤1的逻辑代码")

    with allure.step("步骤2"):
        print("步骤2的逻辑代码")

        with allure.step("子步骤2.1"):
            print("子步骤2.1的逻辑代码")

        with allure.step("子步骤2.2"):
            print("子步骤2.2的逻辑代码")

    with allure.step("步骤3"):
        print("步骤3的逻辑代码")

(3)在 Allure 报告中,每个步骤都将被记录和展示,并且可以查看每个步骤的执行状态、日志和截图等信息。这样的步骤管理可以帮助测试人员更好地理解测试过程,快速定位和排查问题

10.4 allure-pytest在报告中添加图片

添加图片可以提供更直观的信息展示,帮助测试人员和利益相关者更好地理解测试结果。

Allure-pytest 测试框架中使用 @allure.attach 装饰器将图片作为附件添加到报告中。通过以下步骤实现在 Allure 报告中添加图片:

  • 将图片准备好,可以是文件路径或者已经编码为 base64 的图⽚内容
  • 使用 allure.attach 方法将图片作为附件添加到报告中
  • 指定附件的名称和类型,通常是 PNG 或 JPEG 格式

通过 Allure-pytest 的 @allure.attach 装饰器添加图片,可以增强报告的可读性和信息传达效果,使测试结果更加直观和清晰

示例:

python 复制代码
import allure


def test():
    image_path = "22.jpg"
    with open(image_path, 'rb') as image_file:
        allure.attach(image_file.read(), name='图片的名称', attachment_type=allure.attachment_type.JPG)

10.5 allure-pytest的异常时添加图片实现

示例:

python 复制代码
【conftest.py】

import pytest
import allure


@pytest.hookimpl(hookwrapper=True)  # 写死的
def pytest_runtest_makereport(item, call):  # 写死的
    outcome = yield
    report = outcome.get_result()
    if report.when == "call":  # 过滤 前置,和后置,只保留运行中的状态
        if report.outcome != "passed":
            """失败截图数据"""
            image_path = "22.jpg"  # 这里你可以换成你的图片路径
            with open(image_path, 'rb') as image_file:
                allure.attach(image_file.read(), name='异常截图', attachment_type=allure.attachment_type.PNG)

10.6 Pytest的数据驱动方案

数据驱动是⼀种常见的测试方案,它允许我们使用不同的测试数据来执行相同的测试用例。这种方法可以帮助我们提⾼测试覆盖率,减少代码冗余,并更好地组织测试代码。

pytest 支持多种数据驱动的方案,下面是两种常见的方法:

(1)使用@pytest.mark.parametrize装饰器:

pytest提供了⼀个装饰器 @pytest.mark.parametrize ,它可以用来将测试函数参数化。通过这个装饰器,我们可以指定多个参数值的组合,pytest会自动为每个组合生成⼀个独立的测试用例

python 复制代码
import pytest


@pytest.mark.parametrize("input1, input2, expected", [
    (1, 2, 3),
    (5, 5, 10),
    (10, -2, 8)
])
def test_addition(input1, input2, expected):
    print("input1的数据=", input1)
    print("input2的数据=", input2)
    print("expected的数据=", expected)
    assert input1 + input2 == expected

在上⾯的示例中, test_addition 函数被参数化为三个参数: input1 , input2和 expected ,@pytest.mark.parametrize 装饰器定义了参数值的组合,每个组合都会生成⼀个独立的测试用例

(2)使用fixtures和数据文件

我们可以结合 pytest 的 fixtures 来实现数据驱动,在这种方法中,我们可以使用 fixture 读取和处理数据文件,并将数据作为参数传递给测试函数

示例:

python 复制代码
# @file: conftest.py
import pytest


@pytest.fixture(params=[
    (1, 2, 3),
    (5, 5, 10),
    (10, -2, 8)
], name="test_data")
def data(request):
    return request.param


# @file: test_001.py


def test(test_data):
    print(test_data)

相关推荐
我的xiaodoujiao9 小时前
API 接口自动化测试详细图文教程学习系列7--相关Python基础知识6
python·学习·测试工具·pytest
忘忧记11 小时前
pytest + YAML + requests`简单实例化
网络·pytest
我的xiaodoujiao16 小时前
API 接口自动化测试详细图文教程学习系列8--测试接口
python·学习·测试工具·pytest
鹿鸣悠悠3 天前
pytest + requests + allure 接口自动化测试框架指南
pytest
忘忧记3 天前
pytest进阶参数化用法
前端·python·pytest
bug_rabbit3 天前
pytest-html 中文乱码问题终极解决方案(Windows版)
windows·html·pytest
庄小法3 天前
pytest
开发语言·python·pytest
工具人55554 天前
pytest练习
pytest
好家伙VCC4 天前
# Pytest发散创新:从基础测试到智能断言的实战进阶指南在现代软
java·python·pytest