pytest - 基础使用教程

一、pytest介绍

pytest是一个常用的python测试框架,它提供了丰富的功能和灵活的用法,使得编写和

运行测试用例变得简单而高效。其他的测试框架还有unittest、robot framework等。

1.pytest的优点

  1. 语法简洁:编写测试用例非常友好。
  2. 支持参数化测试:允许使用不同的参数多次运行同一个测试函数,提高了测试效率。
  3. 丰富的插件生态系统:可以通过插件扩展各种功能,覆盖率测试、测试报告生成、失败用例重复执行等。此外pytest还支持与selenium、requests、appinum等结合,实现Web自动化、接口自动化、App自动化测试。
  4. 灵活性高:fixture机制替代固定的setUp/tearDown,支持多作用域、按需复用,适配复杂测试场景。

2.pytest的安装

python 复制代码
pip install pytest

3.pytest的运行规则

  1. 文件名:必须以test_开头或者_test结尾
  2. 类名:必须以Test开头,并且不能有__init__方法
  3. 方法名:必须以test开头

为什么测试类中不能出现__init__方法?

  • pytest 实例化测试类时,默认不会传递任何参数:pytest会为每个测试方法创建一个独立的测试类实例,且调用构造方法时不带任何参数;
  • 如果自定义__init__,会破坏实例化逻辑 :一旦定义了带参数的__init__,pytest实例化时会因缺少参数报错;即使是无参的__init__,也可能覆盖pytest注入的核心功能(比如 fixture 上下文)。

4.pytest的参数命令

|---------------------------|-------------------|--------------------|
| 命令 | 描述 | 备注 |
| pytest | 运行当前目录及其子目录里所有的测试 | |
| pytest -s | 显示测试中的print语句 | |
| pytest -v | 增加输出的详细程度 | |
| pytest 文件名 | 只运行指定的测试文件 | |
| pytest 路径名 | 运行指定目录下所有的测试 | |
| pytest -k "关键词" | 执行名称包含关键词的测试 | |
| pytest 文件名::测试类::测试方法 | 精准执行单个测试方法 | |
| pytest --html=report.html | 生成HTML格式的测试报告 | 需要安装pytest-html插 件 |
| pytest --cov | 测量测试覆盖率 | 需要安装pytest-cov插 件 |

二、pytest的配置文件

在了解了pytest的参数命令后我们可以发现,每次运行pytest时都要重新输入这些参数,为了避免参数命令过长以及重复的问题,我们可以将这些参数统一放到一个配置文件中。

这个配置文件就是pytest.ini文件,该文件文件通常位于项目的根目录下。通过在pytest.ini中定义配置项,可以覆盖pytest的默认行为,以满足项目的需求。

以下为常见的配置选项:

|------------------|-------------------------|
| 参数 | 解释 |
| addopts | 指定在命令行中默认包含的选项 |
| testpaths | 指定搜索测试的目录 |
| python_files | 指定发现测试模块时使用的文件匹配模式 |
| python_classes | 指定发现测试类时使用的类名前缀或模式 |
| python_functions | 指定发现测试函数和方法时使用的函数名前缀或模式 |
| norecursedirs | 指定在搜索测试时应该避免递归进入的目录模式 |
| markers | 定义测试标记,用于标记测试用例 |

示例:详细输出cases的文件名下以test_ 开头且方法名以Test 开头的所有用例

python 复制代码
[pytest]
addopts = -vs
testpaths = ./cases
python_files = test_*.py
python_classes = Test*

# 这样配置好后,只需要在终端输入pytest即可

三、pytest的断言

断言(assert)是一种调试辅助工具,用于检查程序的状态是否符合预期,pytest可以使用Python中提供的assert语句来测试预期和值。

基本语法如下:

python 复制代码
assert 条件, 错误信息

# 条件:必须是一个布尔表达式
# 错误信息:当条件为假时显示的错误信息,可选

示例:

python 复制代码
#断言整数
a = 1
b = 2
assert a == b


#断言字符串
str = "hello"
assert "hello" == str


# 断言列表
expect_list = [1, 'apple', 3.14]
actual_list = [1, 'apple', 3.14]
assert expect_list == actual_list


# 断言元组
expect_tuple = (1, 'apple', 3.14)
actual_tuple = (1, 'apple', 3.14)
assert expect_tuple == actual_tuple


# 断言字典
expect_dict = {'name': 'Alice', 'age': 25}
actual_dict = {'name': 'Alice', 'age': 25}
assert expect_tuple == actual_tuple


# 断言集合
expect_set = {1, 2, 3, 'apple'}
actual_set = {1, 2, 3, 'apple'}
assert expect_set == actual_set


# 断言函数
def divide(a, b):
   assert b != 0, "除数不能为0"
   return a / b

print(divide(10, 2)) # 输出 5.0
print(divide(10, 0)) # 抛出 AssertionError: 除数不能为0

四、pytest的前后置

在前面我们说过,pytest的测试类中不能出现__init__方法,那么该如何进行初始化呢?

在测试框架中,前后置是指在执行测试用例前和测试用例后执行一些额外的操作,这些操作可以用于设置测试环境、准备测试数据等,以确保测试的可靠性

pytest提供了三种前后置的方法,适用于不同的情况:

  • setup_method和teardown_method:这两个方法用于类中的每个测试方法的前置和后置操作
  • setup_class和teardown_class:这两个方法用于整个测试类的前置和后置操作
  • fixture:这是pytest推荐的方式来实现测试用例的前置和后置操作。fixture提供了更灵活的控制和更强大的功能。

1.setup_method和teardown_method

从图中我们可以看到,有两个方法,使用setup_method和teardown_method后这两个方法分别进行一次前后置

2.setup_class和teardown_class

从图中我们可以看到,有两个方法,使用setup_class和teardown_class后这一个类只进行一了次前后置

3.fixture

pytest中的fixture是一种强大的机制,用于提供测试函数所需的资源或上下文。它可以用于

设置测试环境、准备数据等。

注意:需要通过使用@pytest.fixture装饰器来告诉pytest一个特定函数是一个fixture

以下用两个例子进行展示,分别是使用fixture作为前置(初始化)和使用普通方法作为前置(初始化):

python 复制代码
import pytest

# 使用fixture作为前置
@pytest.fixture
def fixture_01():
    print('使用fixture进行前置....')

def test_1(fixture_01):
    print('运行测试1')


# 使用普通方法作为前置
def fixture_02():
    print('使用普通方法进行前置....')

def test_2():
    fixture_02()
    print('运行测试2')

结果可以看到,虽然都可以进行前置操作,但是前者可以将函数名作为参数进行调用,而后者需要在方法体中调用。

因此测试脚本中存在的很多重复的代码、公共的数据对象时,使用fixture最为合适。

1.fixture的嵌套

fixture可以进行嵌套使用,即fixture也可以使用其他的fixture

python 复制代码
import pytest

@pytest.fixture
def fixture_01():
    return "a"

@pytest.fixture
def fixture_02(fixture_01):
    return [fixture_01]

# 调用fixture_02
def test_1(fixture_02):
    fixture_02.append("b")
    print(fixture_02)

2.请求多个fixture

一个方法中不但可以请求一个fixture,也可以请求多个

python 复制代码
import pytest

@pytest.fixture
def fixture_01():
    return "a"

@pytest.fixture
def fixture_02():
    return ["a", "b", "c"]

# 调用fixture_01和fixture_02
def test_1(fixture_01, fixture_02):
    assert fixture_01 in fixture_02

3.yield fixture

当我们运行测试时,我们希望确保它们能够自我清理,以便它们不会干扰其他测试(同时也避免留下大量测试数据来膨胀系统)。pytest中的fixture提供了一个非常有用拆卸系统,它允许我们为每个fixture 定义具体的清理步骤。

yield fixture使用yield而不是return 。有了这些fixture,我们可以运行一些代码,并将对象返回给请求的fixture/test,就像其他fixture一样。唯一的不同是:

• return 被替换为yield 。

• 该fixture 的任何拆卸代码放置在yield 之后。

一旦pytest确定了fixture的线性顺序,它将运行每个fixture直到它返回或yield,然后继续执行列表中的下一个fixture做同样的事情。

测试完成后,pytest将逆向遍历fixture列表,对于每个yield的fixture,运行yield语句之后的代码。

4.fixture的不同参数

pytest.fixture(scope='', params='', autouse='', ids='', name='')

参数详解:

scope ------ 参数用于控制fixture的作用范围,决定了fixture的生命周期。可选值有:

  • function (默认):每个测试函数都会调用一次fixture。
  • class :在同一个测试类中共享这个fixture。
  • module :在同一个测试模块中共享这个fixture。(一个文件里)
  • session :整个测试会话中共享这个fixture。

autouse ------ 参数默认为False 。如果设置为True ,则每个测试函数都会自动调用该fixture, 无需显式传入

params ------ 参数用于参数化fixture,支持列表传入。每个参数值都会使fixture执行一次,类 似于for循环

ids ------ 与params配合使用,为每个参数化实例指定可读的标识符(给参数取名字)

name ------ 参数用于为fixture显式设置一个名称。如果使用了name,则在测试函数中需要使 用这个名称来引用 fixture(给fixture取名字)

scope的用法

scope="function" :每个函数都会调用一次fixture

python 复制代码
import pytest

@pytest.fixture(scope="function")
def fixture():
    print("初始化....")

    yield

    print("清除数据....")

class TestExample:
    def test_01(self, fixture):
        print("第一个测试用例")

    def test_02(self, fixture):
        print("第二个测试用例")

scope="class" :一个类中调用一次fixture

python 复制代码
import pytest

@pytest.fixture(scope="class")
def fixture():
    print("初始化....")

    yield

    print("清除数据....")

class TestExample:
    def test_01(self, fixture):
        print("第一个测试用例")

    def test_02(self, fixture):
        print("第二个测试用例")

在使用scope="mould"和scope="session"时我们需要注意,因为这两个参数是作用在不同文件的情况下的,所以可以使用到conftest.py,conftest的介绍如下:

  • conftest.py是一个单独存放的夹具配置文件,名称是固定的不能修改
  • 可以在项目中的不同目录下创建多个conftest.py 文件,每个conftest.py 文件都会对其所在目录及其子目录下的测试模块生效
  • 在不同模块的测试中需要用到conftest.py的前后置功能时,不需要做任何的import导入操作
  • 作用:可以在不同的.py文件中使用同一个fixture函数

已知现在的结构如图所示:

这是scope="mould"和scope="session"的contest.py:

python 复制代码
# scope="module"时的conftest.py
import pytest

@pytest.fixture(scope="module")
def fixture():
    print("初始化....")

    yield

    print("清除数据....")



# scope="session"时的conftest.py
import pytest

@pytest.fixture(scope="session")
def fixture():
    print("初始化....")

    yield

    print("清除数据....")

test_01.py和test_02.py因为代码相同,所以不再重复展示:

python 复制代码
# test_01.py的代码
def test_case01(fixture):
    print("单独放出来的测试用例01")

class TestCase01():
    def test_01(self,fixture):
        print("第一个测试用例")
    def test_02(self,fixture):
        print("第二个测试用例")


# test_02.py的代码
def test_case02(fixture):
    print("单独放出来的测试用例02")

class TestCase02():
    def test_01(self,fixture):
        print("第一个测试用例")

    def test_02(self,fixture):
        print("第二个测试用例")

以下是scope="mould"和scope="session"的结果展示:

autouse的用法

autouse默认为False ,即当前的fixture需要手动显示调用,在该案例之前我们默认使用的都是autouse=False

当autouse=True时,fixture会在所有测试函数执行之前自动调用,无论这些测试函数是否显

式地引用了该fixture

python 复制代码
import pytest

@pytest.fixture(scope="class",autouse=True)
def fixture():
    print("初始化....")

    yield

    print("清除数据....")

class TestExample:
    # 这里不再显示调用fixture
    def test_01(self):
        print("第一个测试用例")

    # 这里不再显示调用fixture
    def test_02(self):
        print("第二个测试用例")
params的用法

五、pytest的参数化

参数化设计是自动化设计中的一个重要组成部分,它通过定义设计参数和规则,使得设计过程更加灵活和可控。

关于pytest的参数化,有两种实现方式,第一种就是通过fixture,fixture更适合需要动态数据和资源管理的复杂场景 ;第二种则是通过pytest.mark.parametrize装饰器,parametrize更适合单个的、简单的场景。

1.在用例上使用参数化

这里parametrize装饰器定义了三个不同的(input,result) 元组,以便test_add函数将依次使用它们运行三次。

2.在类上使用参数化

3.对整个文件使用参数化

要对模块中的所有测试进行参数化,你可以将pytestmark全局变量赋值:

4.自定义参数化

六、pytest的执行顺序的运行

在使用pytest进行测试时,有时候我们需要按照特定的顺序来运行测试用例,尤其是在涉及到测试用例之间的依赖关系时。

pytest本身并不直接支持通过配置来改变测试用例的默认运行顺序,pytest-order是一个第三方插件,专门用于控制测试用例的执行顺序。

安装该插件的方法如下:

python 复制代码
pip install pytest-order

当你需要调整顺序时,只需要在测试用例上加上**@pytest.mark.order(你想要的顺序)**即可

相关推荐
问道飞鱼8 小时前
【自动化测试】pytest 语法与核心概念
自动化测试·pytest·playwright
zUlKyyRC9 小时前
基于一阶RC模型,FFRLS+EKF算法的电池SOC在线联合估计Matlab程序
pytest
我的xiaodoujiao2 天前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 40--完善优化 Allure 测试报告显示内容
python·学习·测试工具·pytest
我的xiaodoujiao2 天前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 41--自定义定制化展示 Allure 测试报告内容
python·学习·测试工具·pytest
nvd112 天前
Pytest 异步数据库测试实战:基于 AsyncMock 的无副作用打桩方案
数据库·pytest
nvd113 天前
深入分析:Pytest异步测试中的数据库会话事件循环问题
数据库·pytest
程序员雷叔3 天前
在postman设置请求里带动态token,看看这两种方法!
selenium·测试工具·单元测试·测试用例·pytest·lua·postman
花酒锄作田4 天前
[python]Flask - Tracking ID的设计
python·flask·pytest
测试秃头怪4 天前
Python测试框架Pytest的参数化
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest