GUI自动化测试详解(三):测试框架pytest完全指南

一、pytest

1.1Python 主流测试框架对比

模块名 含义 查看 / 配置方式
TREND 测试结果的 "趋势图" 需多次执行用例后才有数据,首次执行为空
ENVIRONMENT 测试环境信息(系统版本、Python 版本、接口地址等) 在allure-results目录下添加environment.properties文件即可填充内容;未配置时显示空
CATEGORIES 用例的 "失败分类"(如 "产品 bug""测试脚本 bug") 仅当有用例失败时显示分类数据,全部通过则为空
FEATURES BY STORIES 按 "功能模块 / 用户故事" 分类的用例统计 代码中通过@allure.feature/@allure.story装饰器标记用例;未标记时点 "Show all" 仅能看到默认分类
EXECUTORS 执行测试的 "机器 / 执行人" 信息 默认为空,需通过 Allure 额外配置(如 Jenkins 集成时可自动填充)

1.2pytest 的安装

  • 版本要求pytest 8.3.2需要 Python 3.8 及以上;

  • 安装命令

    复制代码
    pip install pytest==8.3.2  
安装前后的代码差异
  • 未安装 pytest :需要写main函数手动调用测试用例,运行时是普通 Python 脚本
  • 安装 pytest 后 :测试方法名(如test01)前会出现 "运行标记",可直接点击运行,且运行后会显示测试结果(通过 / 失败)。

1.3⽤例运⾏规则

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

注意:Python类中不可以添加init⽅法

复制代码
class Test():
 def __init__(self):
 print("-----init-------")
 def test_a(self):
 print("-----test_a----")

1.4pytest命令参数

pytest 提供了丰富的命令⾏选项来控制测试的执⾏。以下是⼀些常⽤的pytest 命令⾏参数及其使⽤说明。

命令 描述 备注
pytest 在当前目录及其子目录中搜索并运行测试 -
pytest -v 增加输出的详细程度 -
pytest -s 显示测试中的 print 语句 -
pytest test_module.py 运行指定的测试模块 -
pytest test_dir/ 运行指定目录下的所有测试 -
pytest -k 只运行测试名包含指定关键字的测试 -
pytest -m 只运行标记为指定标记的测试 -
pytest -q 减少输出的详细程度 -
pytest --html=report.html 生成HTML格式的测试报告 需要安装 pytest-html 插件
pytest --cov 测量测试覆盖率 需要安装 pytest-cov 插件
示例1:运⾏符合运⾏规则的⽤例
复制代码
pytest
示例2:详细打印,并输⼊print内容
复制代码
pytest -s -v 或者 pytest -sv (可以连写)
示例3:指定⽂件/测试⽤例
复制代码
#指定⽂件:pytest 包名/⽂件名 
pytest cases/test_01.py
#指定测试⽤例: pytest 包名/⽂件名::类名::⽅法名 
pytest cases/test_01.py::Test::test_a

1.5pytest配置⽂件

当我们既要详细输出,⼜要指定⽂件时,命令会⼜臭⼜⻓,⽽且每次运⾏都需要⼿动输⼊命 令,如何解决? 将需要的相关配置参数统⼀放到 pytest 配置⽂件中。

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

参数 解释
addopts 指定在命令行中默认包含的选项。
testpaths 指定搜索测试的目录。
python_files 指定发现测试模块时使用的文件匹配模式。
python_classes 指定发现测试类时使用的类名前缀或模式。
python_functions 指定发现测试函数和方法时使用的函数名前缀或模式。
norecursedirs 指定在搜索测试时应该避免递归进入的目录模式。
markers 定义测试标记,用于标记测试用例。
示例:详细输出 cases 包下⽂件名以 test_ 开头且⽅法名以 Test 开头的所有⽤例
复制代码
[pytest]
addopts = -vs
testpaths = ./cases
python_files = test_*.py
python_classes = Test*

配置好 pytest.ini ⽂件后,命令⾏执⾏ pytest 命令即可,⽆需再额外指定其他参数:

pytest.ini ⽂件 通常位于项⽬的根⽬录下 。通过在pytest.ini 中定义配置项,可以覆盖pytest的默认⾏为,以满⾜项⽬的需求。

1.6前后置

遗留问题:使⽤pytest框架,测试类中不可以添加init()⽅法,如何进⾏数据的初始化

在测试框架中,前后置操作 是指在测试用例执行前、执行后分别运行的辅助操作,核心作用是配置测试环境、准备测试数据、清理测试资源,以此保障测试的可靠性和一致性。

pytest 框架提供三种实现前后置的方式

  1. setup_method/teardown_method :作用于测试类内的每个测试方法,每个用例执行前后都会触发对应的操作。
  2. setup_class/teardown_class :作用于整个测试类,仅在类中所有用例执行前、执行后各触发一次。
  3. fixture:pytest 推荐的实现方案,支持函数、类、模块等多维度作用域,功能灵活且强大。
示例1: setup_method 和 teardown_method
复制代码
import pytest
class TestExample:
   def setup_method(self):
       print("Setup: Before each test")
   def teardown_method(self):
       print("Teardown: After each test")
   def test_example1(self):
       print("Running test_example1")
   def test_example2(self):
       print("Running test_example2")
示例2:setup_class 和 teardown_class
复制代码
class TestExample:
   def setup_class(self):
       print("Setup: Before all test")
   def teardown_class(self):
       print("Teardown: After all test")
   def test_example1(self):
       print("Running test_example1")
   def test_example2(self):
       print("Running test_example2")
1.7断⾔

断言(assert)是 Python 的调试工具 ,通过校验程序状态是否符合预期来检测逻辑错误:条件为假时触发AssertionError异常。pytest 测试中可直接使用 Python 标准assert语句,验证测试结果是否符合预期。

复制代码
assert 条件, 错误信息
  • 条件 :必须是⼀个布尔表达式。
  • 错误信息 :当条件为假时显⽰的错误信息,可选。
示例1:基本数据类型的断⾔
复制代码
#断⾔整数 
a = 1
b = 2
assert a == b
#断⾔字符串 
str = "hello"
assert "hello" == str
示例2:数据结构断⾔
复制代码
def test():
 # 断⾔列表 
 expect_list = [1, 'apple', 3.14]
 actual_list = [1, 'apple', 3.14]
 # 断⾔元组 
 expect_tuple = (1, 'apple', 3.14)
 actual_tuple = (1, 'apple', 3.14)
 
 # 断⾔字典 
 expect_dict = {'name': 'Alice', 'age': 25}
 actual_dict = {'name': 'Alice', 'age': 25}
 
 # 断⾔集合 
 expect_set = {1, 2, 3, 'apple'}
 actual_set = {1, 2, 3, 'apple'}

 assert expect_list == actual_list
 assert expect_tuple == actual_tuple
 assert expect_dict == actual_dict
 assert expect_set == actual_set
示例3:函数断⾔
复制代码
def divide(a, b):
 assert b != 0, "除数不能为0"
 return a / b

# 正常情况 
print(divide(10, 2)) # 输出 5.0 

# 触发断⾔ 
print(divide(10, 0)) # 抛出 AssertionError: 除数不能为0 

1.8参数化

参数化设计是自动化测试的核心设计思路,能让测试过程更灵活可控 ;pytest 中可通过内置的pytest.mark.parametrize装饰器,实现测试函数的参数化配置

示例1:在⽤例上使⽤参数化
复制代码
import pytest
@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), 
("6*9", 42)])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

这⾥, @parametrize 装饰器定义了三个不同的 (test_input,expected) 元组,以便 test_eval 函数将依次使⽤它们运⾏三次。 也可以在类或模块上使⽤ parametrize 标记,这将使⽤参数集调⽤多个函数

示例2:在类上使⽤参数化
复制代码
import pytest
@pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])
class TestClass:
   def test_simple_case(self, n, expected):
       assert n + 1 == expected
   def test_weird_simple_case(self, n, expected):
       assert (n * 1) + 1 == expected
要对模块中的所有测试进⾏参数化,你可以将 pytestmark 全局变量赋值:
复制代码
import pytest
pytestmark = pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])

class TestClass01:
   def test_simple_case(self, n, expected):
       assert n + 1 == expected
   def test_weird_simple_case(self, n, expected):
       assert (n * 1) + 1 == expected
   
class TestClass02:
   def test_simple_case(self, n, expected):
       assert n + 1 == expected
   def test_weird_simple_case(self, n, expected):
       assert (n * 1) + 1 == expected

除了使⽤ @parametrize 添加参数化外, pytest.fixture() 允许对 fixture 函数进⾏参数化。

示例3:⾃定义参数化数据源
复制代码
def data_provider():
 return ["a", "b"]

# 定义⼀个测试函数,它依赖于上⾯函数的返回值 
@pytest.mark.parametrize("data", data_provider())

def test_data(data):
 assert data != None
 print(f"Testing with data provider: {data}")

1.9fixture

pytest 中的fixture 是⼀种强⼤的机制,⽤于提供测试函数所需的资源或上下⽂。它可以⽤于设置测试环境、准备数据等。以下是fixture 的⼀些核⼼概念和使⽤场景.

1.基本使⽤
示例1:使⽤与不使⽤fixture标记

未标记fixture⽅法的调⽤:

复制代码
def fixture_01():
     print("第⼀个fixture标记的⽅法")
def test_01():
     fixture_01()
     print("第⼀个测试⽤例")

fixture标记的⽅法调⽤:

复制代码
import pytest
@pytest.fixture
def fixture_01():
    print("第⼀个fixture标记的⽅法")
def test_01(fixture_01):
    print("第⼀个测试⽤例")

未标记 fixture ⽅法的调⽤与 fixture 标记的⽅法调⽤完全不⼀样,前者需要在⽅法体中调⽤ , ⽽后者可以将函数名作为参数进⾏调⽤ 。 测试脚本中存在的很多重复的代码、公共的数据对象 时,使**⽤ fixture 最为合适.**

示例2:访问列表⻚和详情⻚之前都需要执⾏登录操作
复制代码
import pytest
@pytest.fixture
def login():
     print("---执⾏登陆操作-----")
def test_list(login):
     print("---访问列表⻚")
def test_detail(login):
     print("---访问详情⻚")

通过使⽤@pytest.fixture 装饰器来告诉pytest⼀个特定函数是⼀个fixture,通过运⾏结果可⻅,在执⾏列表⻚和详情⻚之前都会先执⾏ login ⽅法。

2.fixture嵌套
复制代码
# test_append.py的内容 
import pytest

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

@pytest.fixture
def order(first_entry):
     return [first_entry]


def test_string(order):
     order.append("b")
     assert order == ["a", "b"]

pytest 的 fixture 系统具备极高灵活性,是其核心优势之一:测试用例不仅可依赖任意数量的 fixture,fixture 之间也能相互依赖。借助这一特性,可将复杂的测试需求拆解为简洁、结构化的函数 ------ 只需让每个函数声明自身所需的依赖,就能高效实现测试逻辑的解耦与复用。

3.请求多个fixture
复制代码
import pytest
class Fruit:
   def __init__(self, name):
      self.name = name
   def __eq__(self, other):
      return self.name == other.name

@pytest.fixture
def my_fruit():
     return Fruit("apple")

@pytest.fixture
def fruit_basket(my_fruit):
     return [Fruit("banana"), my_fruit]

def test_my_fruit_in_basket(my_fruit, fruit_basket):
     assert my_fruit in fruit_basket

测试和 fixture 不仅限于⼀次请求单个 fixture ,它们可以请求任意多个。

4.yieldfixture

测试需自行清理,避免干扰其他用例、堆积测试数据。pytest 的 Yield fixture 可实现这一需求:

  1. yield替代return,yield 前写初始化逻辑并返回对象;
  2. yield 后编写清理代码;
  3. 执行时先顺行跑 fixture 到 yield 处,测试完成后,再逆行执行 yield 后的清理代码。
示例1:
复制代码
import pytest

@pytest.fixture
def open_close():
    print("前置操作,初始化.....")
    yield
    print("后置操作,清理数据.....")

def test_01(open_close):
    print("第⼀个测试⽤例")
示例2:创建⽂件句柄与关闭⽂件
复制代码
import pytest

@pytest.fixture
def file_read():
     print("打开⽂件句柄")
     fo = open("test.txt", "r")
     yield fo
     print("关闭打开的⽂件")
     fo.close()

def file_write():
     print("打开⽂件句柄")
     fo = open("test.txt","w",encoding="utf-8")

     return fo
     # yield fo
     # print("关闭⽂件句柄") 
     # fo.close()

def test_file(file_write, file_read):
     # 写⼊数据 
     w = file_write
     w.write("测试数据")
     w.close() # 写⼊后关闭⽂件句柄,以便读取 

     # 读取数据 
     r = file_read
     str = r.read(10)
     print("⽂件内容:", str)
5.带参数的fixture
复制代码
pytest.fixture(scope='', params='', autouse='', ids='', name='')

pytest fixture 有 5 个核心参数,作用如下:

  1. scope :控制 fixture 的作用范围(决定生命周期),可选值包括:
    • function(默认):每个测试函数调用一次;
    • class:同一个测试类内共享;
    • module:同一个测试文件内共享;
    • session:整个测试会话中共享。
  2. autouse:默认 False,设为 True 时,所有测试函数会自动调用该 fixture,无需手动传入。
  3. params:实现 fixture 参数化,传列表形式的参数;每个参数值会让 fixture 执行一次(类似循环遍历)。
  4. ids:和 params 配合使用,给每个参数化的实例起一个可读的名称。
  5. name:给 fixture 设置别名;后续测试函数要通过这个别名来引用该 fixture。
示例1:scope 的使⽤

scope="function":

复制代码
import pytest

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

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

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

scope="class":

复制代码
import pytest

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

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

    def test_02(self, fixture_01):
        print("第二个测试用例")
fixture 的 scope 参数核心规则

fixture 的 scope 参数用于控制其作用范围(生命周期):

  1. function(默认值):每个测试函数都会独立调用一次 fixture,执行流程为 "初始化→运行当前测试用例→清理",不同测试函数的 fixture 互不影响;
  2. class:同一测试类内所有测试用例共享该 fixture,仅在类中第一个测试用例执行前完成初始化,等类中最后一个测试用例执行完毕后再执行清理操作;
  3. module/session:可实现更大范围的全局前后置逻辑(需结合多文件场景和 conftest.py 使用)。
conftest.py 结合 fixture 实现全局前后置
  1. 核心作用:将多个测试文件共用的前后置逻辑(如环境初始化、数据清理)写在 conftest.py 中,无需重复编写,实现代码模块化复用;
  2. 关键规则:
    • 文件名必须固定为conftest.py,无法自定义;
    • 可在项目不同目录下创建多个 conftest.py,各自仅对所在目录及子目录的测试文件生效;
    • 测试文件无需手动 import 该文件,可直接使用其中定义的 fixture。
核心逻辑总结

scope 参数决定了 fixture 的生效范围,function 适用于单测试函数的独立前后置,class/module/session 可实现更大范围的共享前后置;而 conftest.py 是全局 fixture 的专属载体,能让通用的前后置逻辑跨测试文件复用,提升代码整洁度。

示例2:scope="moudle" 、 scope="session" 实现全局的前后置应⽤

项⽬结构:

conftest.py

复制代码
import pytest
@pytest.fixture(scope="module", autouse=True)
def fixture_01():
    print("初始化")
    yield
    print("清理")

test_case_01.py:

复制代码
def test_case01():
    print("单独放出来的测试用例01")

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

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

test_case_02.py:

复制代码
def test_case02():
    print("单独放出来的测试用例02")

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

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

当 scope="session" 时:

test_case_01.py:

复制代码
def test_case01():
    print("单独放出来的测试用例01")

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

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

test_case_02.py:

复制代码
def test_case02():
    print("单独放出来的测试用例02")

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

    def test_02(self):
        print("第二个测试用例")
示例3:autouse 的使⽤
复制代码
import pytest

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

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

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

autouse 是 pytest fixture 的核心参数,默认值为 False:

  • 当 autouse=False 时,该 fixture 需要在测试函数中手动显式引用(传参)才能生效,此前示例均采用这一默认方式;
  • 当 autouse=True 时,无需测试函数显式引用,该 fixture 会自动在所有测试函数执行前运行。
示例4:通过 params 实现参数化
复制代码
# 定义一个参数化的 fixture
import pytest

@pytest.fixture(params=["a", "b"])
def data_provider(request):
    return request.param

# 定义一个测试函数,它依赖于上面的参数化 fixture
def test_data(data_provider):
    assert data_provider != None
    print(f"Testing with data provider: {data_provider}")

pytest 中@pytest.mark.parametrize和 fixture 均可实现参数化,二者适用场景不同,选择核心看测试需求:

  1. 若仅需简单参数传递,无复杂资源管理(如数据库连接、文件操作等),优先用parametrize------ 语法简洁直接,适配基础参数化场景;
  2. 若测试需动态加载外部数据,或要管理复杂测试资源(如初始化 / 销毁数据库连接、文件读写等),优先用 fixture------ 支持资源的生命周期管控,适配复杂场景;
  3. 也可结合两者使用,兼顾parametrize的简洁性和 fixture 的资源管理能力。

简言之,parametrize适配简单参数化场景,fixture 适配需动态数据、资源管理的复杂场景。

1.10指定⽤例执⾏顺序

在 pytest 测试中,若测试用例存在依赖关系、需要按特定顺序执行,可借助第三方插件pytest-order实现 ------ 因为 pytest 本身不支持通过配置修改用例默认执行顺序。

第一步:安装插件
复制代码
pip install pytest-order==1.3.0
核心使用逻辑
  1. 基础用法:通过@pytest.mark.order(数字)标记用例,数字越小,用例执行优先级越高;
  2. 适配场景:解决用例间的依赖问题(如先执行数据初始化用例,再执行业务验证用例);
  3. 优势:无需修改 pytest 核心配置,仅通过装饰器即可灵活控制执行顺序。

既可以⽤在测试类上,也可以⽤在测试⽅法上,以测试类为例:

复制代码
@pytest.mark.order(1)
def test_one():
    assert True
 
@pytest.mark.order(2)
def test_two():
    assert True
相关推荐
大神君Bob2 小时前
【AI办公自动化】教你使用Pytho让Word文档处理自动化
python
轻竹办公PPT2 小时前
2025实测!AI生成PPT工具全总结
人工智能·python·powerpoint
彼岸花开了吗2 小时前
构建AI智能体:八十一、SVD模型压缩的艺术:如何科学选择K值实现最佳性能
人工智能·python·llm
tap.AI2 小时前
Deepseek(九)多语言客服自动化:跨境电商中的多币种、多语种投诉实时处理
运维·人工智能·自动化
dagouaofei3 小时前
2026 年工作计划 PPT 制作方式对比:AI 与传统方法差异
人工智能·python·powerpoint
虚拟搬运工3 小时前
xformers造成comfyu启动失败
python·comfyui
Hello.Reader3 小时前
PyFlink DataStream Operators 算子分类、函数写法、类型系统、链路优化(Chaining)与工程化踩坑
前端·python·算法
宇钶宇夕3 小时前
CoDeSys入门实战一起学习(四):应用程序运行、监控与调试
运维·自动化
Learner3 小时前
Python函数
开发语言·python