Python模块pytest

27 篇文章 6 订阅
订阅专栏

目录

1. 简介

2. pytest 的基本用法

2.1. 编写测试函数

2.2. 编写测试类

2.3. 运行测试用例

2.4. 断言

2.5. 生成报告

2.6. 发送邮件

3. 夹具(Fixture)的应用

3.1. Fixture 的语法

3.2. Fixture 的基本用法

3.3. Fixture 的参数化(数据驱动)

① 参数与调用的关系

② 参数化的使用

③ 实例(接口测试)

3.4. Fixture 的作用域

3.5. Fixture 的生命周期

4. 高级用法

4.1. 跳过测试用例

4.2. 预期失败的用例

4.3. 钩子函数

pytest_configure(config) 配置阶段调用

pytest_collection_modifyitems(config, items) 修改测试项

pytest_runtest_logstart(nodeid, location) 用例运行前调用

pytest_collection_finish(session) 测试结束后调用

4.4. 异步多线程


 

1. 简介

  • pytest是一个基于Python编写的轻量级测试框架,用于编写、组织和运行测试用例。它建立在Python标准库中的unittest模块之上,提供了更简洁、灵活和易于使用的测试功能。

pytest的作用

  1. 简化测试用例编写:pytest提供了简洁的语法和灵活的标记功能,使得编写测试用例更加简单和直观。它不仅支持传统的函数式测试用例,还支持类和方法级别的用例,以及参数化、夹具等高级特性。

  2. 自动查找测试用例:pytest能够自动发现项目中符合约定的测试用例。只需将测试文件命名为test_*.py*_test.py,pytest会自动查找并执行其中的测试函数。

  3. 丰富的断言和校验:pytest内置了丰富的断言方法,用于比较和校验测试结果的正确性。这些断言方法简洁直观,可用于比较对象、序列、异常等各种情况。

  4. 夹具(Fixture)支持:夹具是pytest的一个核心概念,用于在测试用例执行前后进行一系列准备和清理工作。pytest提供了灵活且易用的夹具装饰器,能够为测试用例注入依赖、控制资源、模拟测试环境等。

  5. 参数化测试:pytest支持通过@pytest.mark.parametrize装饰器为测试用例传递不同的参数,实现参数化测试。这样可以避免编写大量类似的测试用例,提高测试覆盖率和代码的复用性。

  6. 报告和测试结果可视化:pytest生成详细的测试报告,可以展示测试用例的执行结果和统计数据。同时,它还支持与其他工具集成,例如Jenkins、Travis CI等,方便实现持续集成和自动化测试。

 

pytest的注意事项

  1. 测试文件的命名规则:pytest默认会自动发现以"test_“开头或以”_test"结尾的测试文件,并执行其中的测试函数。因此,请确保测试文件符合这些命名规则,以便pytest能够正确发现和执行测试用例。

  2. 测试函数的命名规则:测试函数应以"test_"开头,以便pytest能够将其识别为测试用例。同时,命名应具有描述性,清晰地反映测试的目的和功能。

  3. 断言的使用:pytest提供了丰富的断言方法来比较和校验测试结果的正确性。对于每个测试,尽量使用恰当的断言来验证预期结果,并提供有意义的错误消息以便于排查问题。

  4. 公共文件 conftest.py 可以定义全局的测试配置。通过在测试目录的根目录或子目录中创建 conftest.py 文件,可以定义夹具、配置、插件、钩子函数等,以方便地管理和共享这些资源,提高测试的灵活性和可维护性。

  5. 夹具的正确使用:夹具(Fixture)是pytest的重要特性,它可以在测试用例的准备和清理阶段执行代码。当使用夹具时,确保正确使用夹具装饰器@pytest.fixture,并在测试函数或类中以参数的形式使用夹具。

  6. 参数化测试用例:使用@pytest.mark.parametrize装饰器可以实现参数化测试,减少冗余的测试代码。在进行参数化时,请确保谨慎选择参数组合,覆盖各种不同的测试情况。

  7. 跳过测试用例:如果某个测试用例暂时无法通过或不需要执行,可以使用@pytest.mark.skip装饰器来跳过该测试用例。但请注意避免滥用跳过功能,以免隐藏潜在的问题。

  8. 预期失败的用例:有时,某些测试用例提前知道会失败,可以使用@pytest.mark.xfail装饰器来标记这些预期失败的用例。这有助于记录已知的问题,并确保测试报告中正确显示预期失败的情况。

  9. 代码覆盖率:可以使用pytest-cov插件来测量代码的覆盖率,以确保测试用例覆盖了足够的代码路径。这有助于发现可能存在的遗漏测试用例的情况。

  10. 使用命令行选项:pytest提供了许多命令行选项,用于控制测试的运行方式和执行参数。掌握这些选项将能够更好地使用pytest进行测试。

 

2. pytest 的基本用法

2.1. 编写测试函数

编写测试函数的步骤

  1. 命名测试函数:测试函数应以 "test_" 开头,pytest 可以自动识别并执行它们。

  2. 编写测试用例:在测试函数中编写用例来测试代码的各种情况。测试用例应包括输入数据、调用被测试代码的函数或方法,并对其返回结果进行断言。

  3. 使用断言进行验证:在测试用例中使用断言来验证预期结果。断言应该根据预期结果与实际结果的比较进行验证,并可以提供可读性强的错误消息。

示例代码(不需要调用函数,执行pytest自动调用)

def test_001_addition():
    '''第一个测试用例,测试加法'''
    assert 1 + 1 == 2    #断言

def test_002_subtraction():
    '''第二个测试用例,测试减法'''
    assert 2 - 1 == 2    #断言

def test_003_multiply():
    '''第三个测试用例,测试乘法'''
    result = 1 * 2
    assert result == 3, f'预期为3, 实际结果为{result}'    #断言并输出异常结果

def test_004_division():
    '''第四个测试用例,测试除法'''
    result = 1 / 1
    assert result == 1, f'预期为1, 实际结果为{result}'    #断言并输出异常结果,用例通过则不输出

def tmp_005_test():
    '''非以test开头,不会自动执行该函数'''
    assert 1 == 1

执行结果(pytest 文件)

结果信息可以分为4类

1、准备开始,统计用例数。

2、输出失败的函数,以E开头的行表示判断错误的行,并输出错误类型。

3、简要说明失败函数信息。格式为:文件名::函数名 - 异常信息。如果手动设置的失败时抛出的信息,则自动使用自定义的信息;若没有设置,则输出判断的行。

4、统计最终结果:失败数、成功数、耗时。

 

2.2. 编写测试类

  • pytest 的测试类功能,可以组织和管理测试用例,提供更好的代码结构和可读性,共享设置和资源,并利用 pytest 的丰富功能来进行更灵活和精细的测试管理。这将显著提高测试代码的可维护性和可扩展性,以确保软件的质量和稳定性。

类的注意事项

  1. 命名规范:按照 pytest 的约定,测试类以 “Test” 开头,并且测试方法应该以 “test_” 开头。这样 pytest 可以自动识别并运行这些测试。

  2. 方法规定:pytest 不能收集带有 __init__() 构造函数的测试类,并且因此该测试类中的测试方法将不被执行。这是 pytest 的测试发现规则所致。

  3. 独立性和隔离性:每个测试方法都应该是独立的,并且不依赖于其他测试方法的状态。确保每个测试方法都可以独立运行,并且不会受到其他测试方法运行的影响。使用 pytest 提供的夹具来共享和管理测试状态和资源,以确保测试方法的独立性和隔离性。

  4. 测试方法之间的顺序:请注意,pytest 不保证测试方法执行的顺序。确保测试方法之间没有依赖关系,并且可以独立执行。使用夹具的 autouse=True 参数可以帮助确保在每个测试方法运行之前和之后执行一些共享设置和清理操作。

 

示例代码(为2个类分别定义了2条用例)

class TestC01(object):
    '''第一个测试类'''
    def test_001(self):
        assert 1 == 1

    def test_002(self):
        assert 2 == 2

class TestC02(object):
    '''第二个测试类'''
    def test_001(self):
        assert 1 == 1

    def test_002(self):
        assert 2 == 3

执行结果

错误信息:文件名::类名::用例名 - 判断行

 

存在 init 函数的类不被执行

class TestC01(object):
    '''存在init函数的类'''
    def __init__(self):
        self.v1 = 1

    def test_001(self):
        assert self.v1 == 1

 

使用类方法来替代 init 函数

class TestC01(object):
    '''使用类方法来定义公共变量'''
    @classmethod
    def setup_class(cls):
        cls.v1 = 1

    def test_001(self):
        '''判断公共变量是否正确'''
        assert self.v1 == 1

 

2.3. 运行测试用例

直接模块中运行

def test_001():
    '''测试用例1'''
    assert 1 == 1

def test_002():
    '''测试用例2'''
    assert 2 == 2

if __name__ == '__main__':
    pytest.main()    # 使用pytest方法调用当前模块的全部函数

 

命令行运行

'''执行当前目录下及子目录下,所有以test开头的文件,执行类和函数'''
pytest

'''执行某个文件全部类'''
pytest test_module.py

'''指定执行某个文件中的类名'''
pytest test_module.py::TestClass

'''指定某个文件中的类名中的函数名'''
pytest test_module.py::TestClass::test_method

需要注意的是:pytest 在默认情况下只会查找以 “test_” 开头的文件、以 “test_” 开头的函数和以 “Test” 开头的类,但是不包含 __init__() 构造函数的类。

 

2.4. 断言

assert expression:判断表达式是否为真,如果为假,则抛出AssertionError异常。
assert expression, message:与上述断言相同,但可以自定义错误信息。

基本格式

assert 断言语句             #对某条语句断言
assert 断言语句,自定义信息  #若断言结果为失败,则输出自定义信息

相等性断言

assert <a> == <b>        #验证 <a> 是否与 <b> 相等。
assert <a> != <b>        #验证 <a> 是否与 <b> 不相等。
assert <a> is <b>        #验证 <a> 是 <b> 的引用。
assert <a> is not <b>    #验证 <a> 不是 <b> 的引用。

数值断言

assert <a> > <b>    #验证 <a> 是否大于 <b>。
assert <a> < <b>    #验证 <a> 是否小于 <b>。
assert <a> >= <b>   #验证 <a> 是否大于等于 <b>。
assert <a> <= <b>   #验证 <a> 是否小于等于 <b>。

成员关系断言

assert <a> in <b>       #验证 <a> 存在于 <b> 中。
assert <a> not in <b>   #验证 <a> 不在 <b> 中。

字符串断言

assert <string> == <expected>     #验证 <string> 是否与 <expected> 完全相等。
assert <string>.startswith(abc)   #验证 <string> 是否以 <abc> 开头。
assert <string>.endswith(abc)     #验证 <string> 是否以 <abc> 结尾。
assert <substring> in <string>    #验证 <substring> 是否存在于 <string> 中。

容器断言

assert <iterable> == <expected>   #验证 <iterable> 是否与 <expected> 完全相等。
assert <iterable> < <expected>    #验证 <iterable> 是否严格小于 <expected>。
assert <iterable> > <expected>    #验证 <iterable> 是否严格大于 <expected>。
assert <iterable> <= <expected>   #验证 <iterable> 是否小于等于 <expected>。
assert <iterable> >= <expected>   #验证 <iterable> 是否大于等于 <expected>。

异常断言

with pytest.raises(<exception>)   #验证是否抛出了指定的异常类型。
@pytest.mark.xfail   #验证预期失败的测试。

 

2.5. 生成报告

 生成 HTML 格式的报告

pytest --html=报告名.html

 

生成 JUnit XML 格式的报告

pytest --junitxml=报告名.xml

 

2.6. 发送邮件

提取html文件内容,通过outlook邮件发送给某个用户

代码示例

import smtplib
from email.mime.text import MIMEText
from email.utils import COMMASPACE

class TestC01(object):
    '''准备测试用例1'''
    @classmethod
    def setup_class(cls):
        cls.v1 = 1

    def test_001(self):
        assert self.v1 == 1

    def test_002(self):
        assert 2 == 2

class TestC02(object):
    '''准备测试用例2'''
    def test_001(self):
        assert 1 == 1

    def test_002(self):
        assert 2 == 3

'''将执行结果输出到html文件,将html文件内容发送邮件'''
def send_mail():
    '''定义一个发送邮件的方法'''

    smtp_host = "smtp.office365.com"    # SMTP 服务器地址
    smtp_port = 587                     # SMTP 服务器端口
    username = 'xxx@outlook.com'        # 发送账号
    password = '123456'                 # 发送账号密码
    recipients = ['x1@outlook.com', 'x2@outlook.com']   # 接收的账号
    subject = 'pytest测试报告'           # 邮件主题
    html_content = open('report.html', 'r').read()      # 读取html文件

    # 构建邮件
    msg = MIMEText(html_content, 'html', 'utf-8')
    msg['Subject'] = subject
    msg['From'] = username
    msg['To'] = COMMASPACE.join(recipients)

    # 发送邮件
    with smtplib.SMTP(smtp_host, smtp_port) as smtp:
        smtp.starttls()                 # 打开通信
        smtp.login(username, password)  # 登录账号,密码
        smtp.send_message(msg)          # 通过发件人、收件人发送邮件

send_mail()

执行结果(执行时需要指定输出为 html 文件)

邮件信息

 

也可以之间将html当做附件发送

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

class TestC01(object):
    '''准备测试用例1'''
    @classmethod
    def setup_class(cls):
        cls.v1 = 1

    def test_001(self):
        assert self.v1 == 1

    def test_002(self):
        assert 2 == 2

class TestC02(object):
    '''准备测试用例2'''
    def test_001(self):
        assert 1 == 1

    def test_002(self):
        assert 2 == 3

'''将执行结果输出到html文件,将html文件内容发送邮件'''
def send_mail():
    '''定义一个发送邮件的方法'''

    smtp_host = "smtp.office365.com"     # SMTP 服务器地址
    smtp_port = 587                      # SMTP 服务器端口
    smtp_username = "xxx@outlook.com"    # SMTP 邮箱用户名
    smtp_password = "123456"             # SMTP 邮箱密码
    receiver_email = "xxx@outlook.com"   # 收件人邮箱
    subject = "pytest测试报告"           # 邮件主题
    report_file = 'report.html'          # 文件名

    # 构建邮件
    msg = MIMEMultipart()
    msg["From"] = smtp_username
    msg["To"] = receiver_email
    msg["Subject"] = subject

    # 添加正文
    body = MIMEText("请查收附件中的HTML报告")
    msg.attach(body)

    # 添加附件
    with open(report_file, "r") as file:
        attachment = MIMEApplication(file.read(), "html")
    attachment.add_header("Content-Disposition", "attachment", filename=report_file)
    msg.attach(attachment)

    # 发送邮件
    server = smtplib.SMTP(smtp_host, smtp_port) # 连接服务器
    server.starttls()                           # 打开通信
    server.login(smtp_username, smtp_password)  # 登录账号、密码
    server.send_message(msg)                    # 通过发件人、收件人发送邮件
    server.quit()                               # 关闭服务器

send_mail()

执行结果(执行时需要指定输出为 html 文件)

邮件信息

 

 

3. 夹具(Fixture)的应用

  • Fixture 是一个用来为测试函数提供可重复使用的设置和资源的机制。Fixture 提供了测试环境的初始化和清理等功能,可以帮助简化测试代码的编写,并提供了测试数据、配置和其他依赖项等。通过使用 Fixture,你可以在测试函数中使用提供的资源,而不需要在每个测试函数中重复编写相同的代码。

3.1. Fixture 的语法

  • Fixture 在测试函数中通过装饰器 @pytest.fixture 定义,并作为函数参数注入到测试函数中。
'''常见定义方法'''
@pytest.fixture
def func():
    pass
'''参数化'''
@pytest.fixture(params=[1, 2, 3])
def func(request):        # request是固定的关键字
    return request.param  # .param也是固定的关键字
'''定义多个夹具'''
@pytest.fixture
def func1():
    pass

@pytest.fixture
def func2():
    pass

 

3.2. Fixture 的基本用法

基础示例

import pytest

@pytest.fixture
def set_data():
    # 设置部分(准备数据、设置环境)
    print('开始设置数据...')
    data = [1, 2, 3]

    # 使用yield将设置和清理两个部分分开
    yield data

    # 清理部分(释放资源、恢复环境、删除临时文件)
    print('清理环境!')

def test_001(set_data):
    '''定义一个测试用例,使用夹具中的set_data方法'''
    print(set_data)     # set_data中定义了一个变量data,所以它的值也是[1,2,3]
    assert len(set_data) == 4   #断言
  • 在 Fixture 方法中,通过在 yield 语句之前编写设置代码,并在 yield 语句之后编写清理代码。yield 语句之前的代码段用于准备和设置测试环境,而 yield 语句之后的代码段用于清理和恢复测试环境。
  • 通过将设置部分和清理部分分开,Fixture 方法可以在测试函数执行前为其提供必要的设置,并在测试函数执行后进行相应的清理操作。这样可以确保测试函数在干净的环境中运行,并且资源得到适当的释放。

 

使用 Fixture 连接 MySQL

import pytest
import mysql.connector

'''定义一个连接数据库的夹具,作用域为当前模块'''
@pytest.fixture(scope="module")
def db_connection():
    # 在设置阶段建立数据库连接
    connection = mysql.connector.connect(
        host="localhost",
        user="username",
        password="password",
        database="database_name"
    )
    
    # 使用yield将设置和清理两个部分分开
    yield connection

    # 在清理阶段关闭数据库连接
    connection.close()

'''定义一个需要连接数据库的用例'''
def test_query_data(db_connection):
    cursor = db_connection.cursor()            #建立连接
    cursor.execute("SELECT * FROM my_table")   #执行SQL
    result = cursor.fetchall()                 #返回结果
    assert len(result) > 0                     #断言结果
    cursor.close()                             #关闭连接

 

使用 Fixture 设置环境变量

import pytest
import os

'''定义一个设置环境变量的fixture,作用域为整个会话'''
@pytest.fixture(scope="session")
def set_env_variables():
    # 在设置阶段设置环境变量
    os.environ["API_KEY"] = "my_api_key"
    os.environ["DB_HOST"] = "localhost"
    
    # 使用yield将设置和清理两个部分分开
    yield

    # 在清理阶段删除环境变量
    del os.environ["API_KEY"]
    del os.environ["DB_HOST"]

'''定义一个简单的测试用例'''
def test_env_variables(set_env_variables):
    # 在测试函数中访问设置的环境变量
    api_key = os.getenv("API_KEY")
    db_host = os.getenv("DB_HOST")
    # 断言
    assert api_key == "my_api_key"
    assert db_host == "localhost"

 

使用 Fixture 登录接口

@pytest.fixture()
def login():
    '''登录接口'''

def test_001(login):    # 使用夹具方法
    '''测试用例1,需要登录接口'''

def test_002():         # 不需要使用夹具方法
    '''不需要登录接口'''

 

3.3. Fixture 的参数化(数据驱动)

① 参数与调用的关系

  • 当参数个数为 0,所有用例调用 1 次
  • 当参数个数为 n,所有用例调用 n 次

举例(设置3个参数,则每个用例调用3次)

import pytest

# Fixture 参数列表
@pytest.fixture(params=[1, 2, 3])
def set_data(request):
    data = request.param
    yield data

def test_001(set_data):
    '''使用夹具的测试用例'''
    assert set_data == 1

def test_002():
    '''不使用夹具的测试用例'''
    assert 1 == 1

Fixture 方法 set_data 中定义了3个参数,则使用 set_data 的用例将会执行3次(每调用1次,依次使用一个参数),而没有使用 set_data 的用例仅调用1次。

 

使用参数后,夹具方法的数据将不可被len使用

import pytest

# Fixture 参数列表
@pytest.fixture(params=[1])
def set_data(request):
    data = request.param
    yield data

def test_001(set_data):
    print(type(set_data))      # 打印类型
    assert len(set_data) == 1  # 断言len

 

② 参数化的使用

参数直接作用到装饰器

import pytest

'''直接将参数作用到装饰器'''
@pytest.fixture(params=[1, 2, 3])
def set_data(request):
    data = request.param
    yield data

def test_001(set_data):
    '''使用夹具的测试用例'''
    assert set_data == 1

 

使用多个 Fixture 参数

import pytest

'''定义第1个Fixture'''
@pytest.fixture(params=[1, 2])
def setup_data(request):
    data = request.param
    yield data

'''定义第2个Fixture'''
@pytest.fixture(params=[4, 5])
def setup_data2(request):
    data = request.param
    yield data

'''使用多个 Fixture 参数'''
def test_function(setup_data, setup_data2):
    assert setup_data != setup_data2

执行次数将按笛卡尔积方法来确定 Fixture 的调用次数,执行流程如下:

  1. 1 对比 4
  2. 1 对比 5
  3. 2 对比 4
  4. 2 对比5

 

通过 @pytest.mark.parametrize 去组合多种参数

import pytest

'''定义第1个Fixture'''
@pytest.fixture()
def setup_data(request):
    return request.param

'''定义第2个Fixture'''
@pytest.fixture()
def setup_data2(request):
    return request.param

'''组合多个Fixture参数'''
@pytest.mark.parametrize("setup_data, setup_data2", [(1, 1), (2, 2), (3, 4), (4, 3)])
def test_001(setup_data, setup_data2):
    assert setup_data == setup_data2

通过 @pytest.mark.parametrize 去指定 Fixture 方法名,并每次分别传入参数

 

通过一个变量去指定参数

import pytest

var = [1, 2, 3]

'''获取变量来定义Fixture参数'''
@pytest.fixture(params=var)
def set_data(request):
    data = request.param
    yield data

def test_001(set_data):
    '''使用夹具的测试用例'''
    assert set_data < 3

 

通过一个函数去指定参数

import pytest

def get_data():
    return [1, 2, 3]

'''获取函数的参数来定义Fixture参数'''
@pytest.fixture(params=get_data())
def set_data(request):
    data = request.param
    yield data

def test_001(set_data):
    '''使用夹具的测试用例'''
    assert set_data < 3

 

③ 实例(接口测试)

import pytest

interface_address = ['接口地址1', '接口地址2', '接口地址3']

'''将接口地址按参数的方式驱动'''
@pytest.fixture(params=interface_address)   # 指定参数
def set_data(request):
    return request.param  # 返回接口地址

'''每次根据一个地址,执行一次用例(按状态码判断)'''
def test_001(set_data):
    result = 200          # 执行接口地址,返回状态码(举例返回的是200)
    assert result == 200  # 断言状态码为200

 

3.4. Fixture 的作用域

设置作用域的语法

import pytest
@pytest.fixture(scope="类型")

1、函数级别的作用域(默认)

  • 每个测试函数都会调用 Fixture 一次。当多个测试函数需要相同的 Fixture 实例时,可以使用函数级别作用域。
@pytest.fixture(scope="function")
def fixture():
    pass

def test_func1(fixture):
    # 测试函数 1

def test_func2(fixture):
    # 测试函数 2

2、 类级别的作用域

  • Fixture 实例对于类中的所有测试函数均为共享。
  • Fixture 会在类的所有测试函数执行前创建,并在所有测试函数执行完后进行清理。
@pytest.fixture(scope="class")
def fixture():
    pass

class TestClass:
    def test_func1(self, fixture):
        # 测试函数 1

    def test_func2(self, fixture):
        # 测试函数 2

3、模块级别的作用域

  • Fixture 实例在整个模块中都为共享。
  • Fixture 会在模块开始前创建,并在模块结束后进行清理。
@pytest.fixture(scope="module")
def fixture():
    pass

def test_func1(fixture):
    # 测试函数 1

def test_func2(fixture):
    # 测试函数 2

4、会话级别的作用域

  • Fixture 实例在整个测试会话期间都为共享。
  • Fixture 会在测试会话开始前创建,并在测试会话结束后进行清理。
@pytest.fixture(scope="session")
def fixture():
    pass

def test_func1(fixture):
    # 测试函数 1

def test_func2(fixture):
    # 测试函数 2

 

3.5. Fixture 的生命周期

Fixture 的生命周期分为3个阶段

1、设置(Setup)阶段:

  • 在该阶段,Fixture 实例被创建并设置为可供测试函数使用的状态。
  • 可以在 Fixture 方法中进行数据准备、资源初始化和环境设置等操作。

2、使用(Use)阶段:

  • 在该阶段,在测试函数中使用 Fixture,获取 Fixture 方法返回的值。
  • 测试函数通过在参数列表中包含 Fixture 的名称来调用 Fixture。

3、清理(Teardown)阶段:

  • 在该阶段,对于具有范围(作用域)的 Fixture,执行清理操作以释放资源和还原环境。
  • 清理操作可以包括关闭文件、断开连接、删除临时文件等等。

 

按作用域来区分生命周期

  • 函数级别(function)作用域的 Fixture:

    • 在每个测试函数之前创建 Fixture 实例。
    • 在测试函数完成后清理 Fixture 实例。
    • 每个测试函数都会获得 Fixture 实例的独立副本。
  • 类级别(class)作用域的 Fixture:

    • 在整个测试类开始前创建 Fixture 实例。
    • 在所有测试函数完成后清理 Fixture 实例。
    • 所有测试函数共享同一个 Fixture 实例。
  • 模块级别(module)作用域的 Fixture:

    • 在整个模块开始前创建 Fixture 实例。
    • 在整个模块结束后清理 Fixture 实例。
    • 整个模块中的所有测试函数共享同一个 Fixture 实例。
  • 会话级别(session)作用域的 Fixture:

    • 在整个测试会话开始前创建 Fixture 实例。
    • 在整个测试会话结束后清理 Fixture 实例。
    • 整个测试会话期间所有模块和测试函数共享同一个 Fixture 实例。

 

 

4. 高级用法

4.1. 跳过测试用例

使用 pytest.mark.skip 装饰器跳过用例

import pytest

@pytest.mark.skip(reason="跳过的原因,在报告中展示")
def test_001():
    assert 1 == 1

def test_002():
    assert 2 == 2

def test_003():
    assert 3 == 3

 

使用 pytest.skip() 函数跳过测试用例

def test_001():
    '''直接跳过'''
    pytest.skip("跳过的原因")
    assert 1 == 1

def test_002():
    '''条件跳过'''
    if 2 > 1:
        pytest.skip("跳过的原因")
    assert 2 == 2

def test_003():
    assert 3 == 3

 

使用 pytest.mark.skipif 装饰器,根据条件跳过测试用例

import pytest
import sys

@pytest.mark.skipif(sys.platform == 'win32', reason='不适用于 Windows 平台')
def test_001():
    assert 1 == 1

def test_002():
    assert 2 == 2

 

4.2. 预期失败的用例

1、标记失败用例

直接将用例标记失败

@pytest.mark.xfail
def test_001():
    assert False

通过条件判断将用例标记失败

@pytest.mark.xfail(sys.platform == 'win32', reason='不适用于 Windows 平台')
def test_002():
    assert False

当引发特定异常时,标记用例失败

import pytest

def func():
    raise ValueError

@pytest.mark.xfail(raises=ValueError)
def test_func():
    func()

 

2、异常处理

自定义异常示例1

import pytest

def divide(a, b):
    if b == 0:
        raise ValueError("除数不能为零")      # 如果被除数为0,抛出自定义异常
    return a / b

def test_divide():
    with pytest.raises(ValueError) as excinfo:  # 检查异常是否被引发
        divide(10, 0)                           # 设置被除数为0
    assert str(excinfo.value) == "除数不能为零"  # 断言引发的异常结果,如果没有异常则用例失败

自定义异常示例2

import pytest

'''自定义异常类'''
class CustomException(Exception):
    pass

'''调用自定义的异常类'''
def func():
    raise CustomException("这是一个自定义异常")

'''判断func是自定义的异常'''
def test_exception_with_match():
    with pytest.raises(CustomException, match="自定义"):
        func()

 

4.3. 钩子函数

pytest_configure(config) 配置阶段调用

  • 这个钩子函数在配置阶段调用,可以用来进行全局的初始化设置。可以在 conftest.py 文件中定义此钩子函数。
  • 在 conftest.py 文件中定义的 pytest_configure 钩子函数将会在 pytest 运行的配置阶段自动调用。这意味着每次运行 pytest 时,都会执行该钩子函数来进行初始化和配置。
  • conftest.py 文件是一个特殊的文件名,pytest 会自动识别它并加载其中的钩子函数和共享的夹具。所以确保将这个文件放置在你的项目中测试目录的根目录或者子目录中。

 将方法写入公共 conftest.py 文件中

def pytest_configure(config):
    # 添加自定义的配置项
    config.addinivalue_line("markers", "slow: mark test as slow")
    config.addinivalue_line("markers", "smoke: mark test as smoke test")

    # 设置日志配置
    setup_logging()

    # 执行其他全局初始化操作
    initialize_database()

def setup_logging():
    # 设置日志级别和格式等
    pass

def initialize_database():
    # 初始化数据库连接等
    pass
  • pytest_configure 钩子函数添加了两个自定义的标记 slow 和 smoke,用于标记测试用例的类型。可以使用 @pytest.mark.slow 或 @pytest.mark.smoke 来选择性地运行测试。
  • pytest_configure 钩子函数调用了 setup_logging() 和 initialize_database() 函数来设置日志配置和初始化数据库连接,以确保在运行测试之前进行了必要的全局初始化操作。

 

pytest_collection_modifyitems(config, items) 修改测试项

  • pytest_collection_modifyitems(config, items): 这个钩子函数在收集测试用例之后进行调用,可以用来进行动态修改测试项目的列表。可以在 conftest.py 文件中定义此钩子函数。
    • config:pytest 的配置对象,可以用于获取和修改框架配置。
    • items:收集到的测试项列表,每一个测试项是一个 pytest.Item 对象,代表一个测试用例或测试集。

过滤测试项(根据某些条件过滤掉不需要执行的测试项)

def pytest_collection_modifyitems(config, items):
    filtered_items = []
    for item in items:
        if not should_skip(item):
            filtered_items.append(item)
    items[:] = filtered_items

添加测试项(添加一些额外的测试项到收集的列表中)

def pytest_collection_modifyitems(config, items):
    extra_item = create_extra_item()
    items.append(extra_item)

修改测试项的排序(根据需求对测试项进行排序,例如按名称、标签、文件路径等进行排序)

def pytest_collection_modifyitems(config, items):
    items.sort(key=lambda item: item.name)

 

pytest_runtest_logstart(nodeid, location) 用例运行前调用

  • pytest_runtest_logstart 的作用是在测试项开始执行时记录相关的日志信息,例如测试项的名称、位置等。
    • nodeid:测试项的标识符,通常是一个字符串,可以用来唯一标识测试项。
    • location:测试项的位置信息,通常是一个元组 (filename, lineno, function),表示测试项所在的文件名、行号和函数名。
def pytest_runtest_logstart(nodeid, location):
    # 获取测试项的文件名、行号和函数名
    filename, lineno, function = location

    # 打印测试项的开始执行事件
    print(f"开始测试: {nodeid}")
    print(f"定位信息: 文件名[{filename}], 行号[{lineno}], 函数名[{function}]")

 

pytest_collection_finish(session) 测试结束后调用

  • pytest_collection_finish 的作用是在测试集合完成后,对整个测试运行过程进行一些全局的定制操作,例如生成报告、统计测试项数量等。
  • session 表示当前的测试运行会话对象。
def pytest_collection_finish(session):
    # 统计收集到的测试项数量
    total_items = len(session.items)

    # 输出测试项数量
    print(f"测试用例总数为: {total_items}")

 

4.4. 异步多线程

  • @pytest.mark.asyncio 是 Pytest 框架中的装饰器,用于支持在测试中使用异步代码和协程。它可以与 Python 的内置异步库 asyncio 结合使用,实现多线程执行异步测试用例。
  • 异步测试用例需要运行在支持异步编程的执行环境下,如 Python 3.7+。确保环境和依赖库能正确支持异步测试。
import pytest
import asyncio

'''异步用例'''
@pytest.mark.asyncio
async def test_001():
    # 异步操作
    await 异步操作
    # 等待异步完成
    await asyncio.sleep(1)
    # 断言
    assert 1 == 2

'''异步用例'''
@pytest.mark.asyncio
async def test_002():
    # 异步操作
    await 异步操作
    # 等待异步完成
    await asyncio.sleep(1)
    # 断言
    assert 2 == 2

'''非异步用例'''
def test_003():
    assert 3 == 3
  • 在测试用例中,可以使用 async 关键字定义异步函数,并使用 await 关键字执行异步操作。
  • 如果测试用例涉及到网络请求或其他I/O操作,可能需要使用适当的异步库(如 aiohttp)或模拟库(如 pytest-aiohttp)来模拟异步行为。

异步等待时间注意点

  1. 根据测试用例的目标和预期结果,选择合适的等待时间。
  2. 考虑并发测试或性能测试时,设置适当的等待时间也很重要。比如:需要模拟更大的并发负载或更高的性能需求,适当缩短 asyncio.sleep 的等待时间,以增加测试的压力。
  3. 设置过短或过长的 asyncio.sleep 时间都可能带来问题。太短的等待时间可能导致测试用例在异步操作完成之前就进行断言,从而导致测试结果不准确。而太长的等待时间可能会使测试用例的执行时间变长,降低整体测试效率。
  4. 在实际场景中,可以根据具体的需求和测试结果进行多次尝试和调整来确定适合的 asyncio.sleep 时间。这样可以获得更准确和高效的异步测试用例。

Python单元测试Pytest官方文档
01-20
pytest是一款基于python实现的自动化测试框架。通过pytest可以方便的实现测试用例的组织与发现、测试执行、测试断言、测试结果汇总输出等。 ## pytest启动方式 pytest启动分为命令行启动和代码启动两种方式。 命令行启动使用pytest外加pytest支持的各种参数来使用,内置的参数可以通过pytest -h来查看,这里不展开介绍。只介绍下几种基本的启动命令: **命令行启动** - 直接输入pytest ,不加任何参数:会从命令输入的当前目录开始查找并执行用例 - pytest+测试模块(py文件),如pytest test_mod.py 执行该文件下的所有符合条件的用例 - pytest+目录,如pytest testdir/ : 递归搜索并执行该目录下所有的测试用例 - 通过"::"字符来指定具体的测试方法,如pytest testmod.py::test_func,或者pytest test_mod.py::TestClass::test_method:执行命令行指向的测试方法 **代码启动** 代码启动的方式,则是在测试代码
Pytest之收集用例规则与运行指定用例详解
HUA1211的博客
10-19 1167
小伙伴们大家好呀,今天笔者会给大家讲解一下pytest是如何收集我们写好的用例?我们又有哪些方式来运行单个用例或者批量运行用例呢?下面将为大家一一解答!
十分钟带你看懂——Python测试框架之pytest最全讲
OKCRoss的博客
06-05 3171
pytest 是一个全功能的 Python 测试工具,可以帮助您编写更好的程序。它与 Python 自带的 unittest 测试框架类似,但 pytest 使用起来更简洁和高效,并且兼容 unittest 框架。pytest 支持简单的单元测试和复杂的功能测试,可以结合 requests 实现接口测试,结合 selenium、appium 实现自动化功能测试,使用 pytest 结合 Allure2 集成到 Jenkins 中可以实现持续集成。
pytest使用详解
热门推荐
chuntingting的博客
10-07 1万+
pytest是一个python的单元测试框架,也称为用例框架。作用:1)发现测试用例。从多个py文件中按照一定的规则找到测试young2)执行测试用例3)判断测试结果,运用python断言4)生成测试报告,可以使用allure、pytest-html、pytest-testreport。
Pytest自动化测试指定执行测试用例
kk_lzvvkpj的博客
11-20 386
打开cmd,进入项目目录
pytest-生命周期钩子函数
weixin_44153651的博客
05-24 478
1、增加自定义参数 def pytest_addoption(parser): # pytest的钩子函数,自动执行 # 增加自定义参数 parser.addoption( "--browser_type", action="store", default="chrome", help="浏览器类型" ) 2、定制化自带的html报告 def pytest_html_results_summary.
软件测试/测试开发丨Pytest测试用例生命周期管理-Fixture
伤心的辣条
05-15 122
模拟 setup,teardown(一个用例可以引用多个 fixture)yield 的用法作用域( session,module, 类级别,方法级别 )自动执行 (autouse 参数)conftest.py 用法,一般会把 fixture 写在 conftest.py 文件中(这个文件名字是固定的,不能改)实现参数化下方这份完整的软件测试视频学习教程已经整理上传完成,需要的朋友们可以自行领取【保证100%免费】
Python生命周期说明
weixin_48186491的博客
04-26 1616
Python的变量生命周期很神奇: python能够改变变量作用域的代码段是def、class、lamda。 if/elif/else、try/except/finally、for/while 并不能涉及变量作用域的更改。 也就是说,在我们普通写函数代码的时候,如果一旦定义,就是函数全局的情况,强制一些跨代码段也可以访问! 搜索顺序为:本地变量->全局变量 ...
Python自动化测试实战篇(11),pytest测试用例生命周期管理,fixture用法详解
入佛门六根不净,入商场狼性不足
07-03 443
Python自动化测试实战篇(11),pytest测试用例生命周期管理,fixture用法详解
python+requests+pytest+allure+yaml+DDT+logs 接口自动化框架终极版
06-07
1pytest插件,运行规则以及参数,pytest.ini配置文 件,跳过用例,用例执行顺序,夹具等 2Fixture固件,contest.py,断言以及Allure报告生成! 3Allure报告定制以及Parametrize数据驱动 4requests模块详解以及Cookie...
python+requests+pytest 接口自动化框架(1-4)
06-02
python+requests+pytest 接口自动化框架 1 pytest插件,运行规则以及参数,pytest.ini配置文 件,跳过用例,用例执行顺序,夹具等 2 Fixture固件,contest.py,断言以及Allure报告生成 3 Pytest测试框架之Allure报告...
pythonpytest收集用例规则与运行指定用例详解
12-26
上篇文章相信大家已经了解了pytest在cmd下结合各种命令行参数如何运行测试用例,并输出我们想要看到的信息。那么今天会讲解一下pytest是如何收集我们写好的用例?我们又有哪些方式来运行单个用例或者批量运行用例呢...
码尚 python+requests+pytest+allure+yaml+DDT+logs 接口自动化框-各分支源码总汇
06-07
1pytest插件,运行规则以及参数,pytest.ini配置文 件,跳过用例,用例执行顺序,夹具等 2Fixture固件,contest.py,断言以及Allure报告生成! 3Allure报告定制以及Parametrize数据驱动 4requests模块详解以及Cookie...
pytestpytest的几种运行方式,尤其最后一种调试很方便
不积跬步无以至千里,不积小流无以成江海~
06-19 1976
(4)运行的规则:不管是主函数的模式运行该,命令行模式,都会区读取这个配置文件。pytest.main('[-vs],','py文件的路径')pytest.main('[-vs]'),'测试目录的路径')pytest.ini是pytest单元测试框架中的核心配置文件。python_functions 方法的命名规则 **python_files 模块的命名规则 xx.py。pytest py文件的路径::类名。(3)作用:改变pytest的默认行为。(1)位置:一般是放在项目的根目录。
pytest测试用例运行方式
onefishwish的博客
05-25 1097
(4)通过nodeId指定用例运行,nodeId由路径::类名::函数名组成 pytest.main([“-vs”,“./excel_demo/test_main.py::test_02”])(4)通过nodeId指定用例运行pytest -vs ./excel_demo/test_main.py::/test_main.py::test_02。(3)指定目录:pytest.main([“-vs”,“./excel_demo/test_main.py”])(1)运行所有:pytest.main()
如何mysql数据导入到mongdb
codemami的博客
05-30 1063
由于MySQL和MongoDB的数据模型不同(例如,MySQL使用关系模型,而MongoDB使用文档模型),你可能需要转换数据的格式。使用MongoDB驱动程序:你也可以使用MongoDB的官方驱动程序(如Python的pymongo)来编写脚本,将数据直接插入到MongoDB中。注意:如果你的JSON文件包含多个文档,并且它们不是作为数组的一部分(即每个文档都在其自己的行上),则需要使用--jsonArray选项。手动转换:对于小型数据集,你可以手动编辑SQL或CSV文件,将其转换为JSON格式。
在linux服务器上使用tensorboard,错误记录
最新发布
wwwwzm的博客
05-30 799
1. 使用tensorboard命令时,不是从虚拟环境中找tensorboard,而是从(全局路径)中找(/home/ljx/.local/lib/python3.9/site-packages/tensorboard)是一个在 Unix-like 系统(包括 Linux 和 macOS)的命令行界面(如 Bash shell)中使用的命令。2.使用which命令, 查看使用的tensorboard的路径,发现使用的是全局路径,不是虚拟环境路径。是一个特殊的变量,它定义了操作系统搜索可执行文件的目录。
Pandas03
Bianca427的博客
05-27 1470
聚合计算时新增一列计算最大值与平均值的差值df.groupby('district').agg(最低工资=('salary', 'min'), 最高工资=('salary', 'max'), 平均工资=('salary', 'mean'), 最大值与均值差值=('salary', myfunc)).rename_axis(["行政区"])
深入理解Python中None和““的区别
m0_54701273的博客
05-27 939
Python的世界里,None和空字符串""经常被用作默认值或用于表示缺省值的情况。尽管它们在某些语境下似乎可互换,但实际上None和""在Python中有着根本的区别。
python+pytest+request 面试题
07-27
当使用Pythonpytest和requests来进行接口测试时,可能会遇到以下面试题: 1. 请解释一下pytest是什么,以及它与Python标准库中的unittest模块有什么区别? 2. 如何在pytest中组织测试用例? 3. pytest中的fixture是什么?它有什么作用? 4. 如何在pytest中使用fixture? 5. pytest中的断言方法有哪些?它们的区别是什么? 6. 如何使用pytest进行接口测试? 7. pytest如何处理异常和错误? 8. 请解释一下如何使用requests库发送HTTP请求,并解释其中的常见方法和参数。 9. 如何在pytest中使用requests库进行接口测试? 10. pytest和requests库在接口测试方面的优势是什么? 这些问题涵盖了pytest和requests库在接口测试方面的基本知识点,希望能帮助你进行面试准备。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
126
原创
1569
点赞
2349
收藏
1048
粉丝
关注
私信
写文章

热门文章

  • Linux命令_grep & 快速查找关键字、文件名 25430
  • Python读取csv、Excel文件生成图表 11523
  • Linux性能监控命令_nmon 安装与使用 9634
  • Linux命令_xargs & 批量杀进程、批量拷贝文件、组合字符串 8773
  • Linux符号大全 7209

分类专栏

  • kingbase 18篇
  • Linux命令 19篇
  • shell 编程 16篇
  • 性能笔记 14篇
  • TPC基准测试 2篇
  • Python 27篇
  • jmeter 13篇
  • 数据库 6篇
  • Linux FAQ 7篇

最新评论

  • shell 数组的详细用法

    你的码,就是我的码: 适合小白入门

  • Linux命令_sed & 快速修改、删除、增加、过滤文件内容

    一只勤劳的耗子: 这才说明它的功能强大

  • Linux命令_sed & 快速修改、删除、增加、过滤文件内容

    萌新03: sed 命令真的好多啊 表情包

  • 虚拟机IP总是自动更改

    blueLecn: 虚拟机里面不存在,/etc/sysconfig/network-scripts/ifcfg-ens33怎么办?我查看了下,我的虚拟机里面根路径etc文件夹,就没有/sysconfig文件夹

  • Python模块multiprocessing & 实现多进程并发

    2301_81750514: 有个问题,如果使用了共享变量,那么在第一个子进程完成任务后第二个任务才能开始,不是和没有用多进程一样了?

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • sysbench安装(在线&离线)
  • KingbaseES临时表
  • KingbaseES数据库物理备份还原sys_rman
2024
05月 5篇
04月 10篇
03月 10篇
02月 5篇
01月 18篇
2023年42篇
2022年36篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

4617作文网给姓何的男生起名字孟姓女宝宝起名周易 生辰八字算命周易取名测字和免费打分梦幻家园破解版无限金币版曹姓女生起名字周易给公司取名大全东字起名公司为产品起个名字周易五行属性查询起名汽修店2019年属猪起名大全修字辈起名周易免费测公司名字打分免费泽起名的寓意宝宝起名三字店铺起名带博字朋友圈怎么起名字怎么算生命数字周公解梦梦见工作丢了周易八字算命软件周公解梦免费看香肠店起名槿汐起名寓意给姓白的女孩起名字算阴命免费全文阅读姓宣女孩起名起名字大师周易取名搜索周公解梦魏 姓 起名字淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻让美丽中国“从细节出发”清明节放假3天调休1天男子给前妻转账 现任妻子起诉要回网友建议重庆地铁不准乘客携带菜筐月嫂回应掌掴婴儿是在赶虫子重庆警方辟谣“男子杀人焚尸”国产伟哥去年销售近13亿新的一天从800个哈欠开始男孩疑遭霸凌 家长讨说法被踢出群高中生被打伤下体休学 邯郸通报男子持台球杆殴打2名女店员被抓19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警两大学生合买彩票中奖一人不认账德国打算提及普京时仅用姓名山西省委原副书记商黎光被逮捕武汉大学樱花即将进入盛花期今日春分张家界的山上“长”满了韩国人?特朗普谈“凯特王妃P图照”王树国3次鞠躬告别西交大师生白宫:哈马斯三号人物被杀代拍被何赛飞拿着魔杖追着打315晚会后胖东来又人满为患了房客欠租失踪 房东直发愁倪萍分享减重40斤方法“重生之我在北大当嫡校长”槽头肉企业被曝光前生意红火手机成瘾是影响睡眠质量重要因素考生莫言也上北大硕士复试名单了妈妈回应孩子在校撞护栏坠楼网友洛杉矶偶遇贾玲呼北高速交通事故已致14人死亡西双版纳热带植物园回应蜉蝣大爆发男孩8年未见母亲被告知被遗忘张立群任西安交通大学校长恒大被罚41.75亿到底怎么缴沈阳一轿车冲入人行道致3死2伤奥运男篮美国塞尔维亚同组周杰伦一审败诉网易国标起草人:淀粉肠是低配版火腿肠外国人感慨凌晨的中国很安全男子被流浪猫绊倒 投喂者赔24万杨倩无缘巴黎奥运男子被猫抓伤后确诊“猫抓病”春分“立蛋”成功率更高?记者:伊万改变了国足氛围奥巴马现身唐宁街 黑色着装引猜测

4617作文网 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化