Pytest精通指南(26)钩子函数-依赖执行(pytest-dependency)


文章目录


前言

pytest-dependency的主要用途是确保测试用例按照指定的依赖关系顺序执行。

在一个复杂的测试套件中,某些测试用例可能依赖于其他测试用例的结果或状态。

pytest-dependency允许明确地定义这些依赖关系,从而确保依赖项先执行,然后再执行依赖于此的测试用例。

这意味着我们可以指定某些测试用例必须在其他测试用例成功执行后才能运行。

通过这种方式,我们可以确保测试的执行顺序与依赖关系得到正确的处理。

应用场景

  • 有序的测试执行 :当测试用例之间存在逻辑依赖关系时,需要确保它们按照正确的顺序执行。
    例如,一个测试用例可能设置了某些数据,而另一个测试用例依赖于这些数据。
  • 资源准备和清理 :在某些情况下,可能需要首先准备一些资源(如数据库连接、文件、网络服务等),并在所有相关测试完成后进行清理。
    pytest-dependency可以帮助你确保资源准备和清理的顺序正确。
  • 条件测试 :我们可能只想在满足特定条件时再执行某些测试用例。
    pytest-dependency可以基于其他测试用例的成功或失败来执行特定的测试。

插件安装

安装命令pip install pytest-dependency

官方文档: https://pytest-dependency.readthedocs.io/en/latest/index.html

注意事项

  • 误解依赖关系 :有些人可能误解了 depends 参数的含义,认为它定义了测试用例的执行顺序,但实际上它定义的是依赖关系,而不是执行顺序。

    执行顺序仍由pytest的内部机制决定,但pytest-dependency插件会在必要时跳过或重新运行测试用例以满足依赖关系。

  • 错误的使用 :如果不正确地使用pytest-dependency,可能会导致测试用例之间的耦合度过高,难以维护和扩展。

    因此,应谨慎使用依赖关系,确保它们确实是必要的。

  • 插件配置问题 :如果没有正确配置pytest-dependency插件,例如未安装插件或未正确地在pytest命令中包含插件,那么依赖关系将不会生效。

  • 并发执行 :如果使用pytest的并发执行功能(如-n参数),请确保依赖关系不会被打乱。
    pytest-dependency和 pytest 的并发执行功能可能不完全兼容,因此需要谨慎使用。

  • 测试隔离:尽量确保每个测试用例是独立的,不依赖于其他测试用例的结果。这样可以提高测试的健壮性和可复用性。

参数分析

@pytest.mark.dependency装饰器接受以下参数,其中最常用的是 depends:

  • depends : 这是一个字符串列表,指定了当前测试用例所依赖的其他测试用例的名称。

    只有当这些依赖项都成功执行时,当前测试用例才会被执行。

  • scope : 这个参数定义了依赖关系的范围。

    默认情况下,依赖关系是在整个测试会话中有效的。

    如果设置为"session", 它会覆盖默认行为。

    如果设置为"function", 则每个函数都会创建一个新的依赖上下文。

  • names: 这个参数允许你为依赖项指定一个自定义名称,这在某些情况下可能很有用,特别是当你有多个依赖项并希望使日志或报告更清晰时。

函数名称依赖实现方式

示例代码

python 复制代码
import pytest

# 模拟数据库
db = {}


# 模拟注册
@pytest.mark.dependency()
def test_signup():
    assert len(db.keys()) == 0
    db.setdefault("username", "root")
    db.setdefault("password", "1234")


# 模拟登录
@pytest.mark.dependency(depends=["test_signup"])
def test_login():
    # 依赖于 test_signup 的测试用例
    assert db.get("username") == "root"
    assert db.get("password") == "1234"

被依赖的测试用例执行成功结果

被依赖的测试用例执行失败结果

类下函数路径实现方式

示例代码

python 复制代码
import pytest


class TestCase:
    @pytest.mark.dependency()
    def test_01(self):
        assert False

    @pytest.mark.dependency(depends=["TestCase::test_01"])
    def test_02(self):
        assert False

执行结果

通过设置别名指定依赖

示例代码

python 复制代码
import pytest

# 模拟数据库
db = {}


# 模拟注册
@pytest.mark.dependency(name="1")
def test_signup():
    assert len(db.keys()) == 0
    db.setdefault("username", "root")
    db.setdefault("password", "1234")


def test_03():
    assert True


# 模拟登录
@pytest.mark.dependency(depends=["1"])
def test_login():
    # 依赖于 test_signup 的测试用例
    assert db.get("username") == "root"
    assert db.get("password") == "1234"


class TestCase:
    @pytest.mark.dependency("name")
    def test_01(self):
        assert False

    @pytest.mark.dependency(depends="name")
    def test_02(self):
        assert False

执行结果

定义依赖范围

从官方说明中我们看到scope可以接受四种参数定义的类型:

  • session
  • package
  • module
  • class

作用于类

scope="class"

  • 作用于所属类,外部类不会被关联

示例代码

python 复制代码
import pytest


class TestCase1:
    @pytest.mark.dependency()
    def test_01(self):
        assert False


class TestCase2:
    @pytest.mark.dependency(scope="class")
    def test_01(self):
        assert True

    @pytest.mark.dependency(depends=["test_01"], scope="class")
    def test_02(self):
        assert True

执行结果

作用于模块

scope="module"

  • 不传递scope参数,即默认参数是module,作用于当前py文件
  • 只会查找当前文件的符合条件的文件名,类里同名的方法不会被依赖

示例代码

python 复制代码
import pytest


@pytest.mark.dependency()
@pytest.mark.xfail(reason="模拟失败")
def test_fail():
    print("进入module::test_fail函数")
    assert False


class TestCase1:
    @pytest.mark.dependency()
    def test_01(self):
        assert False


class TestCase2:
    @pytest.mark.dependency()
    def test_fail(self):
        print("进入TestCase2::test_fail函数")
        assert True

    @pytest.mark.dependency(depends=["test_fail"])
    def test_02(self):
        assert True

执行结果

作用于包

scope="package"

  • 作用于当前目录同级的依赖函数,跨目录无法找到依赖的函数。

test_case_01.py示例代码

python 复制代码
import pytest


class TestCase1:
    @pytest.mark.dependency(scope="class")
    def test_01(self):
        print('测试用例01')
        assert 1 == 1

    @pytest.mark.dependency(depends=['test_01'], scope="class")
    def test_02(self):
        print('测试用例02依赖于测试用例01')
        assert True

test_case_02.py示例代码

python 复制代码
import pytest


class TestCase2:
    @pytest.mark.dependency(
        depends=['testcase/test_case_01.py::TestCase1::test_02'],
        scope="package"
    )
    def test_03(self):
        print('测试用例03依赖于统计目录下test_case_01.py的02')
        assert True

执行结果

作用于会话

scope="session"

  • 作用域全局,可跨目录调用。但被依赖的用例必须先执行,否则用例会执行跳过!

testcase/test_case_01.py示例代码

python 复制代码
import pytest


class TestCase1:
    @pytest.mark.dependency(scope="class")
    def test_01(self):
        print('测试用例01')
        assert 1 == 1

    @pytest.mark.dependency(depends=['test_01'], scope="class")
    def test_02(self):
        print('测试用例02依赖于测试用例01')
        assert True

testcase/test_case_02.py示例代码

python 复制代码
import pytest


class TestCase2:
    @pytest.mark.dependency(
        depends=['testcase/test_case_01.py::TestCase1::test_02'],
        scope="package"
    )
    def test_03(self):
        print('测试用例03依赖于统计目录下test_case_01.py的02')
        assert True

testcase2/test_case_03.py示例代码

python 复制代码
import pytest


class TestCase3:
    @pytest.mark.dependency(
        depends=['testcase/test_case_02.py::TestCase2::test_03'],
        scope="session"
    )
    def test_04(self):
        print('测试用例04依赖于非同级目录下test_case_02.py的03')
        assert True

执行结果

拓展-非常重要

  • 当使用 pytest.mark.parametrize 对测试用例进行参数化时,每个参数组合都会产生一个独立的测试用例实例 ,每个实例都有一个唯一的节点ID(nodeid),这个ID包含了测试用例的路径和参数值。
  • 因此,如果依赖的上下文测试用例使用了参数化 ,那么仅仅通过测试函数的方法名来建立依赖关系是不够的,因为同一个测试函数可能会有多个不同的实例,每个实例的节点ID都是唯一的。
  • 为了解决这个问题,pytest-dependency 插件支持使用节点ID来建立依赖关系,而不仅仅是测试函数的方法名。你可以通过传递节点ID数组来指定依赖关系,而不是简单地传递方法名。

示例代码

python 复制代码
import pytest


@pytest.mark.dependency()
@pytest.mark.parametrize('data', [1])
def test_a(data):
    # data = 1
    assert data == 1


@pytest.mark.dependency(depends=['test_a'])
def test_d():
    assert True


@pytest.mark.dependency(depends=['test_d'])
def test_b():
    assert 'ooo' == 'ooo'


@pytest.mark.dependency(depends=['test_b'])
def test_c():
    assert 'lll' is 'lll'

执行结果-未指定参数化改变的node信息

执行结果-指定参数化改变的node信息

相关推荐
AIAdvocate1 小时前
Pandas_数据结构详解
数据结构·python·pandas
小言从不摸鱼1 小时前
【AI大模型】ChatGPT模型原理介绍(下)
人工智能·python·深度学习·机器学习·自然语言处理·chatgpt
铁匠匠匠1 小时前
从零开始学数据结构系列之第六章《排序简介》
c语言·数据结构·经验分享·笔记·学习·开源·课程设计
架构文摘JGWZ3 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
FreakStudio3 小时前
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
python·嵌入式·面向对象·电子diy
小齿轮lsl3 小时前
PFC理论基础与Matlab仿真模型学习笔记(1)--PFC电路概述
笔记·学习·matlab
Aic山鱼3 小时前
【如何高效学习数据结构:构建编程的坚实基石】
数据结构·学习·算法
qq11561487073 小时前
Java学习第八天
学习
天玑y4 小时前
算法设计与分析(背包问题
c++·经验分享·笔记·学习·算法·leetcode·蓝桥杯
2301_789985944 小时前
Java语言程序设计基础篇_编程练习题*18.29(某个目录下的文件数目)
java·开发语言·学习