Python自动化测试:pytest新手快速入门指南


目录👑

编辑

pytest安装:版本匹配是关键

核心基础:pytest用例运行规则

命令行参数与配置文件

必学技能:前后置操作与断言

前后置操作(解决初始化问题)

断言:验证测试结果的核心

pytest参数化设计@pytest.mark.parametrize

场景1:用例上使用参数化

场景2:类上使用参数化(所有方法共用参数)

pytest核心:fixture的使用(重中之重)

[7.1 基本使用](#7.1 基本使用)

[7.2 嵌套使用](#7.2 嵌套使用)

7.3请求多个fixture

[7.4核心特性:yield fixture(前置+后置)](#7.4核心特性:yield fixture(前置+后置))

[7.5 fixture参数(灵活控制作用范围)](#7.5 fixture参数(灵活控制作用范围))

[7.6 全局fixture:conftest.py](#7.6 全局fixture:conftest.py)


pytest安装:版本匹配是关键

学习pytest的第一步就是安装,这里要注意版本兼容性,避免因版本问题导致后续使用异常。

安装命令很简单,在命令行输入以下代码即可:

复制代码
pip install pytest==8.3.2

如果你的Python版本低于3.8,可以参考以下版本对应关系选择合适的pytest版本:

pytest 版本 最低 Python 版本
8.0+ 3.8+
7.1+ 3.7+
6.2 - 7.0 3.6+
5.0 - 6.1 3.5+
3.3 - 4.6 2.7, 3.4+

安装完成后,记得在PyCharm中更新Python解释器,确认pytest已成功安装。这里有个明显的对比,未安装pytest时,运行测试用例需要手动编写main函数调用;安装后,测试方法名前会出现直接运行标志,无需额外代码,效率大幅提升。

核心基础:pytest用例运行规则

安装完成后,并不是所有方法都能被pytest识别并运行,必须遵循以下3条核心规则(重点记!):

  1. 文件名必须以test_开头或者_test结尾(如test_login.py、login_test.py);

  2. 测试类必须以Test开头,并且不能有__init__方法;

  3. 测试方法必须以test开头(如test_login_success、test_get_user_info)。

这里有个常见坑:测试类中不能定义__init__方法。因为pytest采用自动发现机制收集测试用例,会自动实例化测试类,若定义__init__方法,会掩盖测试逻辑,引入副作用,影响测试结果准确性。

如果测试类需要初始化操作,无需使用__init__,可以选择setup_method/teardown_method、setup_class/teardown_class方法,或后续会讲到的fixture函数,这些都是更合适的替代方案。

示例(错误示范,含__init__方法):

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

运行该代码会出现异常,无法正常执行测试用例,大家一定要避开这个错误。

命令行参数与配置文件

pytest提供了丰富的命令行参数,能灵活控制测试用例的执行,以下是新手最常用的几个参数,建议牢记:

命令 描述 备注
pytest 在当前目录及其子目录中搜索并运行测试
pytest -v 增加输出的详细程度
pytest -s 显示测试中的print语句
pytest 文件名.py 运行指定的测试模块 如pytest test_login.py
pytest -k 关键字 只运行测试名包含指定关键字的用例 如pytest -k login
pytest --html=report.html 生成HTML格式的测试报告 需安装pytest-html插件

实操示例:

  1. 简单运行所有符合规则的用例:pytest(不显示print内容);

  2. 详细输出并显示print内容:pytest -sv(-s和-v可连写);

  3. 指定文件运行:pytest cases/test_01.py;

  4. 指定具体用例运行:pytest cases/test_01.py::Test::test_a。

但如果每次运行都要输入长长的命令,会非常繁琐。解决方案是创建pytest配置文件pytest.ini,将常用参数统一配置,后续只需输入pytest命令即可运行。

配置文件示例(核心配置):

复制代码

将该文件放在项目根目录下,运行pytest命令时,会自动读取配置,无需再手动输入参数,极大提升效率。

必学技能:前后置操作与断言

前后置操作(解决初始化问题)

测试过程中,经常需要在执行用例前做初始化(如打开浏览器、连接数据库),执行后做清理(如关闭浏览器、断开数据库),这就是前后置操作。pytest提供3种方式实现,其中fixture是最推荐的方式。

  1. setup_method 和 teardown_method:针对每个测试方法,执行一次前置和后置(如每个用例前都登录);

  2. setup_class 和 teardown_class:针对整个测试类,只执行一次前置和后置(如类开始前初始化数据库,结束后关闭);

  3. fixture:灵活度最高,可实现全局、局部的前后置,还能实现数据共享(后续详细讲解)。

示例:

python 复制代码
class Testcase02():
    def setup_method(self):
        print("setup_method")

    def test01(self):
        print("test01")
    def test02(self):
        print("test02")

    def teardown_method(self):
        print("teardown_method")

class Test():
    def setup_class(self):
        print("setup_class")

    def teardown_class(self):
        print("teardown_class")

    def test01(self):
        print("test01")
    def test02(self):
        print("test02")

运行结果会显示,在含setup_method 和 teardown_method方法的测试类中,每个测试方法执行前都会打印前置信息,执行后打印后置信息。

在含有setup_class 和 teardown_class方法的测试类中,针对整个测试类,只执行一次前置和后置。

断言:验证测试结果的核心

断言是自动化测试的核心,用于验证实际结果是否符合预期。pytest无需额外导入断言库,直接使用Python原生的assert语句即可,语法简洁,功能强大。

基本语法:

assert 条件, 错误信息(错误信息可选,断言失败时显示)

条件必须是⼀个布尔表达式

常见断言场景示例:

python 复制代码
import requests
import pytest
def test():
    #断言基本数据类型
    a = 1
    b = 1
    assert a== 1

    str1 = "kiku"
    str2 = "kiki"
    assert str1 == str2

def test_ds():
    # 断⾔列表
    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_dict == actual_dict

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

#测试接口返回数据所有内容:字段&字段值
def test2():
    url = "https://jsonplaceholder.typicode.com/posts/1"
    r = requests.get(url = url)
    expect_data = {
      "userId": 1,
      "id": 1,
      "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    }
    actual_data = r.json()
    assert expect_data == actual_data

#对关键字段进行校验
def test3():
    url = "https://jsonplaceholder.typicode.com/comments?postId=1"
    r = requests.get(url = url)

    assert r.json()[0]['id'] == 1
def test4():
    url = "https://jsonplaceholder.typicode.com/"
    text = "Use your own data"
    r = requests.get(url)
    assert text in r.text

上面代码中用到的网站**http://jsonplaceholder.typicode.com/**是一个免费、公开、开箱即用的在线模拟 REST API 服务,专门用来给开发者做测试、学习、演示、原型开发。

它能提供现成的假接口 + 假数据,让我们在没有真实后端的情况下,也能练接口、写代码、做自动化测试。

断言失败时,pytest会清晰显示失败原因,方便我们快速定位问题,这也是pytest的一大优势。

pytest参数化设计**@pytest.mark.parametrize**

在测试中,经常需要用不同的参数重复运行同一个测试用例(如测试登录功能,输入不同的账号密码),如果手动编写多个用例,会造成代码冗余。

pytest内置的**@pytest.mark.parametrize**装饰器,可轻松实现参数化,大幅提升测试效率。

常见使用场景:

场景1:用例上使用参数化
python 复制代码
#对多组参数实现参数化
@pytest.mark.parametrize("input, expected",[("3+5", 8), ("16/4", 4), ("4*9",37)])
def test7(input, expected):
    assert eval(input) == expected

该用例会自动运行3次,分别使用3组参数,无需编写3个独立用例。

场景2:类上使用参数化(所有方法共用参数)
python 复制代码
#在类上实现参数化
@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 全局变量赋值:

python 复制代码
#全局变量参数化
pytestmark = pytest.mark.parametrize("data",(1,2))

class TestA():
    def testA1(self, data):
        print(data)
    def testA2(self, data):
        print(data)
class TestB():
    def testB1(self, data):
        print(data)
    def testB2(self, data):
        print(data)

还可以自定义参数化数据源

python 复制代码
def data_provider():
    #...
    return ["a","b",100]

@pytest.mark.parametrize("data", data_provider())()
def test_data(data):
    print(data)

结合fixture还能实现更复杂的参数化,满足不同测试场景需求。

pytest核心:fixture的使用(重中之重)

fixture是pytest最强大的功能,它可以实现前后置操作、数据共享、资源管理等,比setup/teardown系列方法更灵活、更强大。

7.1 基本使用

使用@pytest.fixture装饰器标记一个函数,该函数就是fixture,测试用例只需将fixture函数名作为参数,即可调用fixture。

python 复制代码
#使用fixture来调用方法
@pytest.fixture
def login():
    print("登录login()")

def test_blogList(login):
    print("博客列表页blogList()")

def test_blogDetail(login):
    print("博客详情页blogDetail()")

运行结果会显示,两个测试用例执行前都会先执行login fixture的前置操作,实现了代码复用。

7.2 嵌套使用

fixture也能嵌套使用:

python 复制代码
#fixture的嵌套
@pytest.fixture
def first():
    return "a"
@pytest.fixture
def second(first):
    return [first]
def test_string(second):
    second.append("b")
    assert second == ["a", "b"]
7.3请求多个fixture

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

python 复制代码
#请求多个fixtrue
class Fruit:
    def __init__(self, name, color):
        self.name = name
        self.color = color
    def __eq__(self, other):
        return self.name == other.name

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

@pytest.fixture
def all_fruits(my_fruit):
    return [Fruit("apple", "green") , Fruit("banana","yellow")]

def test_fruit(my_fruit, all_fruits):
    assert my_fruit in all_fruits
7.4核心特性:yield fixture(前置+后置)

使用yield关键字,可在fixture中同时实现前置和后置操作:yield之前的代码是前置,yield之后的代码是后置,测试用例执行完成后,会自动执行后置代码,实现资源清理。

python 复制代码
#yield fixtrue
@pytest.fixture
def operator():
    print("前置操作:数据的初始化")
    yield 100
    print("后置操作:数据的清理")
def test_01(operator):
    assert operator == 100
    print(100+operator)

def test_02(operator):
    print(100-operator)

常用场景:打开/关闭文件、连接/断开数据库等,避免资源泄露。

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

• return 被替换为 yield 。

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

⼀旦 pytest 确定了 fixture 的线性顺序,它将运行每个 fixture 直到它返回或 yield ,然后

继续执行列表中的下⼀个 fixture 做同样的事情。

测试完成后, pytest 将逆向遍历 fixture 列表,对于每个 yield 的 fixture ,运行yield

语句之后的代码

7.5 fixture参数(灵活控制作用范围)

fixture可通过参数控制作用范围、自动调用等,核心参数如下:

复制代码
pytest.fixture(scope='', params='', autouse='', ids='', name='')
  • scope:作用范围(默认function,表示每个用例调用一次;class:每个类调用一次;module:每个模块(文件)调用一次;session:整个测试会话调用一次);

  • autouse:是否自动调用(默认False,需手动传入;True:所有用例自动调用,无需传入);

  • params:参数化fixture,实现多组参数测试;

  • name:给fixture重命名,调用时需使用新名称。

示例scope,范围function和class:

python 复制代码
import pytest


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

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

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

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

    def test_03(self,fixture_02):
        print("第1个测试用例")

    def test_04(self,fixture_02):
        print("第2个测试用例")

可以看到test_01和 test_02方法都各自执行了前置和后置操作,因为他们fixture的范围scope是function,

而 test_03和 test_04在一个类中 并且fixture范围scope是class,所以fixture只会在第一个方法开始前执行一次,在最后一个方法结束后执行一次。

运行后会发现,无需手动传入fixture,测试类的所有用例都会自动执行前置和后置操作。

另外两个scope范围在7.6中展示。

通过 params 实现参数化示例:

python 复制代码
@pytest.mark.parametrize("data", (1,2,"kiku"))
def test_provider1(data):
    print(data)

@pytest.fixture(params=[1,2,"kiku"])
def provider2(request):
    return request.param

def test_provider2(provider2):
    print(provider2)

我们可以看到通关fixture的params实现参数化的效果和用@pytest.mark.parametrize实现的效果相同。

7.6 全局fixture:conftest.py

如果多个测试模块需要共用fixture,无需重复编写,可创建conftest.py 文件(名称固定,不能修改),将全局fixture放在该文件中,所有子目录的测试用例均可直接调用,无需导入。

可以在项目中的不同目录下创建多个 conftest.py 文件,每个 conftest.py 文件都会对其所在目录及其子目录下的测试模块生效。

在不同模块的测试中需要用到 conftest.py 的前后置功能时,不需要做任何的import导入操作

作用:可以在不同的 .py 文件中使用同⼀个 fixture 函数。


conftest.py文件,范围为module,autouse为True 用例自动调用,不用手动传入

python 复制代码
import pytest


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

文件1:

python 复制代码
def test_01(fixture_01):
    print("第1个测试用例")


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

文件2:

python 复制代码
def test_01():
    print("第1个测试用例")
class Testcase1:
    def test_02(self):
        print("第2个测试用例")

测试结果:

我们看到scope为moudle时,每个课执行的测试文件都调用了一次fixture前置操作和后置操作。


conftest.py文件,范围为session

python 复制代码
import pytest


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

测试文件内容不变,还是上面那两个,执行测试:

我们可以看到整个测试会话中fixture前置操作、后置操作都只执行了一次。

所有 .py 文件都在一个会话里,顺序按文件名跑,前后置只在最开始和最后各跑一次。

相关推荐
西西弗Sisyphus2 小时前
Python Lambda 表达式等价普通函数实现
python·lambda
Agent产品评测局2 小时前
企业邮件处理自动化落地,分类回复全流程实现方法 —— 2026企业级智能体选型与落地全景指南丨Agent产品测评局
运维·人工智能·ai·chatgpt·自动化
张二娃同学2 小时前
深度学习入门:YOLOv5 与 Fast R-CNN的认识
人工智能·python·深度学习·神经网络·yolo
海天一色y2 小时前
深度学习时序预测进阶:CNN-LSTM-MHA混合模型+灰狼优化算法(GWO)实战
python
Yu_Lijing2 小时前
Python数据分析和数据处理库Pandas(Series篇)
人工智能·python·数据分析·pandas
九河_2 小时前
从requirements.txt中安装缺失的包
python·conda·pip·环境管理
llm大模型算法工程师weng2 小时前
Python爬虫实现指南:从入门到实战
开发语言·爬虫·python
AI效率工坊2 小时前
【Python实战】10万行数据自动清洗:pandas+AI智能识别+异常检测完整方案
人工智能·python·pandas
乔江seven2 小时前
LlamaIndex 实现ReAct Agent
前端·python·react.js