pytest 零基础入门实战指南

目录

前言

[一、pytest 安装](#一、pytest 安装)

二、用例收集规则

三、命令行

[1. 常用的 pytest 命令参数](#1. 常用的 pytest 命令参数)

[2. pytest 配置文件](#2. pytest 配置文件)

四、测试前后置

五、断言

[1. 基本类型](#1. 基本类型)

[2. 数据结构](#2. 数据结构)

[3. 函数断言](#3. 函数断言)

六、参数化

[1. 在用例上使用参数化](#1. 在用例上使用参数化)

[2. 在类上使用参数化](#2. 在类上使用参数化)

[3. 在模块上使用参数化](#3. 在模块上使用参数化)

七、fixture

[1. 基本用法](#1. 基本用法)

[2. fixture 嵌套](#2. fixture 嵌套)

[3. 调用多个 fixture](#3. 调用多个 fixture)

[4. yield fixture](#4. yield fixture)

[5. 带参数的 fixture](#5. 带参数的 fixture)

总结


前言

pytest 是 Python 生态中主流、简洁且可扩展的测试框架,用于编写单元测试、集成测试、接口 / UI 自动化测试等。pytest 的语法简洁清晰,对于编写测试用例非常友好。它内置了丰富的断言库,可以轻松地进行测试结果的判断。同时它还支持参数化测试,允许使用不同的参数多次运行同⼀个测试函数,这大大提高了测试效率。 它有着丰富的插件生态系统,可以通过插件扩展各种功能,比如覆盖率测试、失败用例重复执行(如 pytest-rerunfailures 插件)等。此外,pytest 还支持与 selenium、 requests、appinum 等结合,实现 Web 自动化、接口自动化、App 自动化测试。本文介绍 pytest 的基本用法。


一、pytest 安装

pip install pytest==版本号

安装完成之后,可以通过查看安装列表,检查是否安装成功;

pip list

二、用例收集规则

pytest 采用自动发现机制收集测试用例。它会实例化测试类,并调用符合要求的测试方法作为测试用例。

测试用例的默认收集规则:

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

满足上述要求后,可以通过 pytest 指令,一键运行符合要求的测试用例;

三、命令行

1. 常用的 pytest 命令参数

在当前目录及其子目录搜索并运行测试用例

pytest

增加输出的详细程度

pytest -v

显示测试中的 print 语句

pytest -s

运行指定的测试模块

pytest test_module.py

运行指定目录下的所有测试

pytest test_dir/

只运行标记为指定标记的测试

pytest -m <maker>

示例:

指定测试用例:pytest 包名 / 文件名 :: 类名 :: 方法名

pytest -sv testcases/test_login.py::TestLogin::test_01

2. pytest 配置文件

为了避免测试指令过长,每次运行输入不方便,可以将指令配置统一放到 pytest 配置文件中;

在当前项目下创建 pytest.ini ⽂件,该文件为 pytest 的配置文件,以下为常见的配置选项:

指定在命令行中默认包含的选项

addopts

指定搜索测试的目录

testpaths

指定发现测试模块时使用的文件匹配模式

python_files

指定发现测试类时使用的类名前缀或模式

python_classes

指定发现测试函数和方法时使用的函数名前缀或模式

python_functions

定义标记,用于标记测试用例

makers

示例:详细输出 cases 包下文件名以 test_ 开头且类名以 Test 开头的测试用例

pytest

addopts = -vs

testpaths = ./cases

python_files = test_*.py

python_classes = Test*

配置好 pytest.ini 文件后,命令行执行 pytest 命令即可,无需额外指定其它参数;

pytest.ini 文件通常位于项目的根目录下,通过 pytest 的默认行为,以满足项目的需求。

四、测试前后置

在 pytest 框架中,测试类中不能添加 __init__方法,如果要进行数据的初始化,可以采用前后置的方法进行。

前后置的方法有三种:

  1. setup_method 和 teardown_method ,这两个方法用于类中每个测试方法前置和后置操作;

  2. setup_class 和 teardown_class ,这两个方法用于整个类的前置和后置操作;

3.fixture 是 pytest 推荐的实现测试用例前置和后置的操作

示例:

python 复制代码
class TestDemo1:
    # 方法的前置操作
    def setup_method(self):
        print("执行方法的前置操作!")

    #  方法的后置操作
    def teardown_method(self):
        print("执行方法的后置操作!")

    # 类的前置操作
    def setup_class(self):
        print("执行类的前置操作!")

    # 类的后置操作
    def teardown_class(self):
        print("执行类的后置操作!")

    def test_01(self):
        print("执行 test_01!")

    def test_02(self):
        print("执行 test_02!")

运行结果:

五、断言

pytest 允许在测试中使用标准的 python 断言,来验证预期和值;

基本语法:

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

条件:必须是一个布尔表达式;

错误信息:条件为假时,显示的错误信息;

1. 基本类型

python 复制代码
# 整数断言
a = 1
b = 2
assert a == b, "a 和 b 不相等"

# 断言字符串
str = "hello"
assert str == "hello", "str 值不是 hello"

2. 数据结构

python 复制代码
# 断言列表
expect_list = [1, "hello", "1.5"]
actual_list = [1, "hello", "1.5"]
assert  actual_list == expect_list

# 断言元组
expect_tuple = (1, "hello", "1.5")
actual_tuple = (1, "hello", "1.5")
assert actual_tuple == expect_tuple

# 断言字典
expect_dict = {"key1": "value1", "key2": 10}
actual_dict = {"key1": "value1", "key2": 10}
assert actual_dict == expect_dict

# 断言集合
expect_set = {1, 1.5, "hello"}
actual_set = {1, 1.5, "hello"}
assert actual_set == expect_set

3. 函数断言

python 复制代码
def divide(a, b):
    assert b != 0, "除数不能为 0"
    return a / b

print(divide(10, 2))
print(divide(3, 0))

运行结果:

六、参数化

pytest 中内置的 pytest.mark.parametrize 装饰器允许对测试函数的参数进行参数化。

1. 在用例上使用参数化

python 复制代码
import pytest


@pytest.mark.parametrize("input, expected",[("3 + 5", 8), ("4 * 5", 20), ("10 / 2", 5)])
def test_eval(input, expected):
    assert eval(input) == expected

运行结果:

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

也可以在类或模块上使用 parametrize 标记,这将使用参数集调用多个函数。

2. 在类上使用参数化

python 复制代码
@pytest.mark.parametrize("a, b", [(1, 2), (3, 4)])
class TestParametrize:
    def test_add(self, a, b):
        assert a + 1 == b

    def test_add2(self, a, b):
        assert a * 1 + 1 == b

运行结果:

3. 在模块上使用参数化

python 复制代码
import pytest

pytestmark = pytest.mark.parametrize("a, b", [(3, 9), (4, 16)])

class TestParametrize:
    def test_mul1(self, a, b):
        assert a * a == b

    def test_mul2(self, a, b):
        assert math.pow(a, 2) == b

运行结果:

七、fixture

pytest 中的 fixture 用于提供测试函数所需的上下文,可以用于设置测试环境,准备测试数据等;

1. 基本用法

python 复制代码
import pytest

def fixture01():
    print("调用 fixture01")

@pytest.fixture
def fixture02():
    print("调用 fixture02")

class TestFixture:
    def test_fixture01(self):
        fixture01()
        print("执行 test_fixture01")

    def test_fixture02(self, fixture02):
        print("执行 test_fixture02")

运行结果:

未标记和标记 @pytest.fixture 的方法调用方式完全不同,前者需要在方法中调用,后者可以将函数名作为参数调用

适用于存在很多重复代码和公共的数据对象时,进行调用;

例如:

每次获取博客列表内容之前都需要进行登录操作,那么登录操作就可以作为一个 fixture,在获取列表的方法中传递函数名作为参数,进行调用,使得代码更为简洁,可读性和可维护性良好。

2. fixture 嵌套

python 复制代码
@pytest.fixture
def fixture01():
    return "a"

@pytest.fixture
def fixture02(fixture01):
    return [fixture01]

class TestFixture2:
    def test_fixture(self, fixture02):
        fixture02.append("b")

        assert fixture02 == ["a", "b"]

运行结果:

测试可以定义任意数量的 fixture,fixture 也可以调用其它的 fixture。

3. 调用多个 fixture

python 复制代码
@pytest.fixture()
def apple():
    return "apple"

@pytest.fixture
def fruit(apple):
    return [apple, "banana"]

class TestFruit:
    def test_fruit(self, apple, fruit):
        assert apple in fruit

运行结果:

在测试方法中,可以调用任意多个 fixture;

4. yield fixture

当测试执行完毕,我们还需要清理测试数据,复位测试条件,以确保当前测试,不干扰下一个测试;

yield fixture 就是 pytest 提供的拆卸系统

yield fixture 在测试执行完毕后,使用 yield,而不是 return:

    1. return 被替换为 yield;
    1. yield 之后放置系统的拆卸代码;
    1. pytest 确定 fixture 顺序之后,依次运行每个 fixture,直至返回 yield,再继续运行下一个 fixture;
    1. 测试完成后,逆向遍历每一个 fixture 列表,对于每个包含 yield 的 fixture,执行 yield 之后的代码;

示例:前后置操作

python 复制代码
@pytest.fixture
def pre_and_post_handle():
    print("执行前置操作!")

    yield

    print("执行后置操作!")

class TestHandle:
    def test_handle(self, pre_and_post_handle):
        print("执行测试!")

运行结果:

示例:打开与关闭文件

python 复制代码
@pytest.fixture
def write_file():
    file = open("./test.txt", "w", encoding="utf8")

    yield file

    file.close()

@pytest.fixture
def read_file():
    file = open("./test.txt", "r", 1000)

    yield file

    file.close()

class TestFile:
    def test_write_file(self, write_file):
        write_file.write("hello, pytest")
    def test_read_file(self, read_file):
        str = read_file.read()
        print(str)

运行结果:

5. 带参数的 fixture

语法:

python 复制代码
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 的使用

python 复制代码
@pytest.fixture(scope="function")
def fixture01():
    print("fixture01 执行前置操作")
    yield
    print("fixture01 执行后置操作")

@pytest.fixture(scope="class")
def fixture02():
    print("fixture02 执行前置操作")
    yield
    print("fixture02 执行后置操作")

class TestFix:
    def test_fix1(self, fixture01, fixture02):
        print("执行 test_fix1")

    def test_fix2(self, fixture01, fixture02):
        print("执行 test_fix2")

运行结果:

conftest.py 和 @pytest.fixture 结合使用,实现全局的前后置应用;

@pytest.fixture 与 conftest.py 文件结合使用,可以实现在多个测试模块( .py ) 文件中共享前后置操作,这种结合的方式使得可以在整个测试项目中定义和维护通用的前后置逻辑,使测试代码更加模块化和可维护。

规则:

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

示例:module 和 session 的前后置

目录结构:

配置:

复制代码
[pytest]
addopts = -vs
testpaths = ./tests
python_files = test_demo12*.py
python_classes = Test*

conftest.py

python 复制代码
@pytest.fixture(scope="module")
def module_fixture():
    print("module_fixture 前置操作")
    yield
    print("module_fixture 后置操作")

@pytest.fixture(scope="session")
def session_fixture():
    print("session_fixture 前置操作")
    yield
    print("session_fixture 后置操作")

测试类:

python 复制代码
class TestDemo120:
    def test_demo1(self, module_fixture, session_fixture):
        print("执行 test_demo1")
    def test_demo2(self, module_fixture, session_fixture):
        print("执行 test_demo2")
python 复制代码
class TestDemo121:
    def test_demo1(self, module_fixture, session_fixture):
        print("执行 test_demo1")
    def test_demo2(self, module_fixture, session_fixture):
        print("执行 test_demo2")

运行结果:

示例:autouse = True

python 复制代码
@pytest.fixture(autouse=True)
def autouse_fixture():
    print("autouse_fixture 前置操作")
    yield
    print("autouse_fixture 后置操作")

class TestAutouse:
    def test_auto1(self):
        print("执行 test_auto1")

    def test_auto2(self):
        print("执行 test_auto2")

运行结果:


总结

本文全面讲解了 pytest 框架的核心使用内容,从环境安装、用例收集规则入手,介绍了常用命令行参数与配置文件用法。同时依次讲解测试前后置、各类断言、多场景参数化,以及 fixture 的基础用法、嵌套调用、yield 和传参等关键功能。

全文由浅入深,覆盖 pytest 日常测试开发必备知识点,帮助大家快速掌握 pytest 基础用法,高效编写和管理自动化测试用例。

相关推荐
傲笑风5 小时前
【openvino】tinybert基于openvino服务化部署(四)
人工智能·python·自然语言处理·nlp·bert·openvino
维基框架6 小时前
WIKI 知识库 v1.1.1 正式发布
人工智能·python
谢白羽9 小时前
SGLang的AWQ量化笔记
笔记·python·sglang
迷迭香yy10 小时前
基金档案数据工程实战从收入分析到持仓穿透的Python解析 IG50免费开源股票数据API接口
开发语言·python
清水白石00810 小时前
Python 类型设计深度解析:TypedDict 能否替代 dataclass?从 JSON 数据边界到 API 设计的最佳实践
java·python·json
️学习的小王11 小时前
Git项目提交忽略文件怎么做?以Python项目为例,详解.gitignore
git·python·elasticsearch
前端 贾公子11 小时前
第09章:上下文与记忆 (6)
开发语言·前端·python
evans在进步11 小时前
Java 常用设计模式入门:建造者、工厂、单例、外观与代理
java·python·设计模式
jayson.h11 小时前
PDF 合并+添加页码 相关库、类、函数
开发语言·前端·python
典典分享指南12 小时前
用飞书多维表格搭建内容管理看板
java·python·r语言·composer·symfony