pytest1-接口自动化测试场景

课程:B站大学
记录python学习,直到学会基本的爬虫,使用python搭建接口自动化测试,后续进阶UI自动化测试

接口自动化测试


接口自动化测试的场景

手工功能测试、手工接口测试流程:

自动化测试流程:

软件发起请求-得到响应流程:

测试金字塔模型

自动化测试前需要思考什么?

• 自动化测试前,需要提前准备好数据,测试完成后,需要自动清理脏数据,有没有更好用的框架?

• 自动化测试中,需要使用多套测试数据实现用例的参数化,有没有更便捷的方式?

• 自动化测试后,需要自动生成优雅、简洁的测试报告,有没有更好的生成方法?

Pytest是什么?

pytest 能够支持简单的单元测试和复杂的功能测试;

pytest 可以结合 Requests 实现接口测试;结合 Selenium、Appium 实现自动化功能测试;

使用 pytest 结合 Allure 集成到 Jenkins 中可以实现持续集成。

pytest 支持 315 种以上的插件。

Pytest 有哪些格式要求?

Pytest 有哪些格式要求?

• 文件名

• 类

• 方法/函数

pytest命名要求:

在pycharm下安装pytest

打开 PyCharm 下方的 Terminal 标签页

输入以下命令安装 pytest:

bash 复制代码
pip install pytest

验证是否安装成功(可选)

bash 复制代码
pytest --version

这里我选择使用Anaconda Navigator虚拟环境管理用于管理环境,这个软件的好处就是有很多基础库,不好的的地方若其他人需要运行项目但是不想下载Anaconda,那么只能导入.txt依赖

pytest知识点

测试用例示例

python 复制代码
def test_xxx(self):
    # 测试步骤1
    # 测试步骤2
    # 断言 实际结果 对比 预期结果
    assert ActualResult == ExpectedResult

类级别的用例示例

常见的用例,先执行setup进行资源准备,然后执行test_xx用例函数,最后运行teardown清除资源和数据等后置操作

python 复制代码
class TestXXX:
    def setup(self):
        # 资源准备
        pass

    def teardown(self):
        # 资源销毁
        pass

    def test_XXX(self):
        # 测试步骤1
        # 测试步骤2
        # 断言 实际结果 对比 预期结果
        assert ActualResult == ExpectedResult

断言

使用assert进行断言,断言验证接口响应是否符合预期

python 复制代码
def test_a():
    a = 1
    b = 4
    expect = 3
    assert a + b == expect

测试装置介绍

类型 规则
setup_module/teardown_module 全局模块级
setup_class/teardown_class 类级,只在类中前后运行一次
setup_function/teardown_function 函数级,在类外
setup_method/teardown_method 方法级,类中的每个方法执行前后
setup/teardown 在类中,运行在调用方法的前后(重点)

参数化

参数化设计方法就是将模型中的定量信息变量化,使之成为任意调整的参数。对于变量化参数赋予不同数值,就可得到不同大小和形状的零件模型。

参数化测试函数使用

参数化:笛卡尔积

• 比如

• a = [1,2,3]

• b = [a,b,c]

• 有几种组合形式?

• (1,a),(1,b),(1,c)

• (2,a),(2,b),(2,c)

• (3,a),(3,b),(3,c)

python 复制代码
import pytest

# 1) 基本参数化:多个数据行
@pytest.mark.parametrize("a,b,expected", [
    (1, 2, 3),
    (2, 3, 5),
    (0, 5, 5),
])
def test_add(a, b, expected):
    assert a + b == expected


# 2) 使用 ids 给每一行命名,便于输出阅读
@pytest.mark.parametrize("x,y,expected", [
    (2, 2, 4),
    (10, -1, 9),
    (3, 0, 3),
], ids=["small", "mixed", "zero"])
def test_add_with_ids(x, y, expected):
    assert x + y == expected


# 3) 多个 @parametrize 装饰器会计算笛卡尔积(所有组合)
@pytest.mark.parametrize("a", [1, 10])
@pytest.mark.parametrize("b", [0, 5])
def test_cartesian(a, b):
    # 只做简单可重复的断言
    assert (a + b) >= a


# 4) 使用 pytest.param 设置 id 与 marks(例如对某个输入期望失败)
@pytest.mark.parametrize("dividend,divisor,expected", [
    (10, 2, 5),
    pytest.param(10, 0, None, marks=pytest.mark.xfail(reason="division by zero")),
])
def test_division(dividend, divisor, expected):
    # 对 0 做显式处理(这里让它抛出异常以触发 xfail)
    result = dividend / divisor
    assert result == expected


# 5) 间接参数化:把参数传给 fixture 进行预处理
@pytest.fixture
def doubled(request):
    # request.param 来自 indirect 参数传递
    return request.param * 2


@pytest.mark.parametrize("doubled,expected", [
    (1, 2),
    (3, 6),
], indirect=["doubled"])
def test_indirect(doubled, expected):
    assert doubled == expected


# 6) 参数化 fixture:fixture 本身就带有 params,测试会为每个 params 调用一次
@pytest.fixture(params=[("a", 1), ("b", 2)], ids=["pair-a", "pair-b"])
def pair(request):
    return request.param


def test_param_fixture(pair):
    name, val = pair
    assert isinstance(name, str)
    assert isinstance(val, int)


# 7) 使用组合与说明:展示 pytest.param 多种用法
@pytest.mark.parametrize(
    "s,expected_len",
    [
        pytest.param("", 0, id="empty"),
        pytest.param("x", 1, id="one-char"),
        pytest.param("hello", 5, id="hello"),
    ],
)
def test_string_lengths(s, expected_len):
    assert len(s) == expected_len


# 8) 小结性的测试:演示参数化如何简化不同输入的覆盖
@pytest.mark.parametrize("inputs,expected", [
    ([1, 2, 3], 6),
    ([], 0),
    ([10], 10),
])
def test_sum_list(inputs, expected):
    assert sum(inputs) == expected

Mark:标记测试用例

• 场景:只执行符合要求的某一部分用例 可以把一个web项目划分多个模块,然后指定模块名称执行。

• 解决:在测试用例方法上加 @pytest.mark.标签名

• 执行:-m执行自定义标记的相关用例

• pytest -s test_mark_zi_09.py -m=webtest

• pytest -s test_mark_zi_09.py -m apptest

• pytest -s test_mark_zi_09.py -m "not ios"

打标签mark可以运行指定的用例:

python 复制代码
import pytest
import requests

# ===== 1. 定义标记 =====
# 在pytest.ini或conftest.py中注册自定义标记(可选但推荐)
# 示例:在pytest.ini中添加:
# [pytest]
# markers =
#     smoke: 冒烟测试用例
#     regression: 回归测试用例
#     api: 接口测试

# ===== 2. 带标记的测试类 =====
class TestUserAPI:
    # ===== 3. 用例级别标记 =====
    @pytest.mark.smoke          # 冒烟测试标记
    @pytest.mark.api            # 接口测试标记
    def test_get_user_info(self):
        """获取用户信息(正常流程)"""
        url = "https://api.example.com/users/1"
        response = requests.get(url)
        assert response.status_code == 200
        assert response.json()["id"] == 1

    @pytest.mark.regression     # 回归测试标记
    @pytest.mark.parametrize("user_id, expected_status", [  # 参数化标记
        (1, 200),    # 正常用户
        (999, 404)   # 不存在的用户
    ])
    def test_get_user_by_id(self, user_id, expected_status):
        """根据ID获取用户(参数化测试)"""
        url = f"https://api.example.com/users/{user_id}"
        response = requests.get(url)
        assert response.status_code == expected_status

    # ===== 4. 跳过测试标记 =====
    @pytest.mark.skip(reason="该接口已废弃,暂不测试")  # 跳过标记
    def test_deprecated_api(self):
        pass

    # ===== 5. 条件跳过标记 =====
    @pytest.mark.skipif(
        condition=not hasattr(requests, "get"),  # 条件成立时跳过
        reason="requests库缺少get方法"
    )
    def test_conditional_skip(self):
        pass

# ===== 6. 命令行执行示例 =====
"""
# 运行所有冒烟测试
pytest -v -m smoke

# 运行除smoke外的所有测试
pytest -v -m "not smoke"

# 运行参数化测试中的特定数据(需结合pytest参数化标记)
pytest -v -k "test_get_user_by_id and 1"
"""

通过标签我们可以分层测试需要测试的接口信息

Skip:使用场景

Skip 使用场景

  • 调试时不想运行这个用例

  • 标记无法在某些平台上运行的测试功能

  • 在某些版本中执行,其他版本中跳过

  • 比如:当前的外部资源不可用时跳过

  • 调试过程中,需要跳过的用例(比如登出)

    如果测试数据是从数据库中取到的,连接数据库的功能如果返回结果未成功就跳过,因为执行也都报错

解决1:添加装饰器

python 复制代码
pytest.skip(reason)
这里也就是跳过,reason就是原因

适用场景:

用例正在编写中,暂时不想执行

某个功能尚未开发完成

用例暂时不需要跑(如旧用例、备用用例

python 复制代码
@pytest.skip(reason="功能未开发,暂不测试")
def test_unfinished():
    assert True

下方是条件跳过,默认skipif判断为ture

python 复制代码
@pytest.skipif

适用场景:

环境不满足(如测试环境、依赖服务不可用)

依赖未安装 / 模块缺失

接口/功能未上线

仅限特定条件执行(如非生产环境、特定版本等)

这里可以用来指定一些用例的条件,比如A模块中的用例执行完后才能执行B模块中的用例

python 复制代码
import sys
import pytest

@pytest.mark.skipif(sys.version_info < (3, 8), reason="需要 Python 3.8+")
def test_python_version():
    assert True

在用例里面也是可以使用skip进行跳过用例的

pytest命令运行测试用例文件

在编写代码阶段,在windows端我们可以使用pycharm直接运行测试用例文件,但是接口自动化一般都是在服务端运行,故一般运行时采用命令行运行。

  • pytest------ 运行所有测试
  • pytest -v------ 显示详细日志(推荐日常调试用)
  • pytest -s------打印输出日志(-vs,打印输出详细日志)
  • pytest 文件.py------ 运行指定测试文件
  • pytest -m smoke------ 只运行标记为 smoke 的用例
  • pytest -k "login"------ 按用例名称关键字筛选执行
  • pytest -x------ 遇到失败立即停止,快速排查问题
  • pytest -n 4------ 多进程并行执行,提升测试速度
  • pytest --junitxml=report.xml------ 生成标准测试报告,用于 CI/CD
    服务端调试使用
python 复制代码
pytest 文件名.py::函数名

运行指定文件中的某个测试函数,如 pytest test_login.py::test_login_success

一般在本地开发完自动化测试代码后,就可以直接git上传,然后服务端进行部署接口测试项目,后续集成CI/CD【Jenkins】,就可以自动执行pytest -m xxxx命令,执行对应的标签用例。

故自动化测试项目的标签很好用。

一般来说一个业务场景就写成一个用例文件,多个用例文件有执行顺序,这时候可以用

pytest中执行顺序如何调整

pytest中py文件执行顺序如何调整

对于pytest文件的指定顺序,可以进行CI/CD持续集成,比如Jenkins调用可以写个shell脚本

shell脚本的顺序就是py文件执行的顺序

shell 复制代码
#!/bin/bash

# 按你希望的顺序执行测试文件
pytest tests/test_login.py
pytest tests/test_order.py
pytest tests/test_user.py

pytest中测试类中的test用例执行顺序如何调整

在测试函数上添加装饰器 @pytest.mark.order(数字)

python 复制代码
# test_order.py

import pytest

@pytest.mark.order(2)
def test_b():
    print("执行 test_b")

@pytest.mark.order(1)
def test_a():
    print("执行 test_a")

若不用order,那么python是自上而下运行的执行顺序,可以结合skip进行跳过操作

pytest中常见的异常

常见异常类型

  • AssertionError:断言失败时抛出
  • FixtureLookupError:找不到指定的 fixture 时抛出
  • ImportError:导入模块或测试文件失败时抛出
  • TimeoutError:测试超时时抛出(需配合插件使用)
  • PytestFailedException:pytest 内部测试失败异常

python中捕获异常

1、使用 try-except 捕获:

python 复制代码
def test_example():
    try:
        # 可能出错的代码
        assert 1 == 2
    except AssertionError as e:
        print(f"断言失败: {e}")
        raise  # 重新抛出异常使测试仍标记为失败

2、 pytest.raises 上下文管理器(推荐使用方式):

python 复制代码
import pytest

def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        1 / 0  # 预期会抛出 ZeroDivisionError

比如在调试业务中会常常见到token过期的异常,此时可以使用异常断言

python 复制代码
def test_exception():
    with pytest.raises(ValueError) as excinfo:
        int("abc")
    assert "invalid literal" in str(excinfo.value)  # 检查异常信息

这里的raises用于异常,那么只要符合异常就直接会运行通过,这里不会打印抛出日志,我一般使用try-except语句抛出指定的异常信息

数据驱动测试(参数化)

数据驱动是指通过改变数据来驱动自动化测试的执行,从而引起测试结果的改变。简单来说,这是参数化的一种应用。对于数据量较小的测试用例,可以通过代码参数化实现数据驱动;而在数据量较大的情况下,建议使用结构化的文件(如 yaml、json 等)来存储数据,并在测试用例中读取这些数据。

参数的好处:数据驱动、数据隔离、不影响代码逻辑

目录结构:

python 复制代码
api_tests/
├── test_api.py      # 测试代码(核心逻辑)
└── test_data.yaml   # 测试数据(YAML 格式)

test_data.yaml:

python 复制代码
# 只需维护这一份数据,格式:case_name, 请求参数, 预期结果
- case_name: "登录成功"
  url: "https://httpbin.org/post"
  method: "POST"
  data: {"username": "admin", "password": "123456"}
  expected: {"status_code": 200}

- case_name: "登录失败"
  url: "https://httpbin.org/post"
  method: "POST"
  data: {"username": "wrong", "password": "123"}
  expected: {"status_code": 200}

每个测试用例包含:case_name(用例名)、url(接口地址)、method(HTTP方法)、data(请求参数)、expected(预期结果)。

直接复用 httpbin.org模拟真实 API(实际项目替换成你的接口地址)。

test_api.py:

python 复制代码
import pytest
import requests
import yaml

# 1. 读取 YAML 数据(1行代码)
test_data = yaml.safe_load(open("test_data.yaml"))  

# 2. 参数化测试(1个函数搞定所有用例)
@pytest.mark.parametrize("case", test_data, ids=lambda x: x["case_name"])  # 用例名显示在报告中
def test_api(case):
    # 发请求(自动适配 POST/GET)
    resp = requests.request(case["method"], case["url"], json=case["data"])
    
    # 断言(只检查状态码,可扩展)
    assert resp.status_code == case["expected"]["status_code"]

运行命令:

python 复制代码
pytest test_api.py -v

实践是检验真理的唯一标准

相关推荐
secondyoung4 小时前
Mermaid流程图高效转换为图片方案
c语言·人工智能·windows·vscode·python·docker·流程图
2401_861277554 小时前
分层架构系统测试的主要要点
功能测试·系统架构·单元测试·集成测试·模块测试
nini_boom4 小时前
**论文初稿撰写工具2025推荐,高效写作与智能辅助全解析*
大数据·python·信息可视化
1***s6325 小时前
Python爬虫反爬策略,User-Agent与代理IP
开发语言·爬虫·python
咖啡の猫5 小时前
Python的自述
开发语言·python
重启编程之路6 小时前
python 基础学习socket -TCP编程
网络·python·学习·tcp/ip
云和数据.ChenGuang7 小时前
pycharm怎么将背景换成白色
ide·python·pycharm
我的xiaodoujiao8 小时前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 25--数据驱动--参数化处理 Excel 文件 2
前端·python·学习·测试工具·ui·pytest