【pytest学习总结2.3 】- 如何使用固定装置 fixtures (1)

news2024/12/22 19:56:15

目录

2.3 如何使用固定装置 fixtures

2.3.1 请求并调用固定装置 fixture

2.3.2 自动固定装置(不需要你请求调用)

2.3.3 固定装置的范围:function, class, module, package or session.

2.3.4 测试后置 teardown/cleanup

2.3.5 安全的 teardown

2.3.6 安全的运行多个断言语句

2.3.7 固定装置可以处理请求的内容【看不懂】

🎁更多干货

完整版文档下载方式:


2.3 如何使用固定装置 fixtures

2.3.1 请求并调用固定装置 fixture

  1. 在基本级别上,测试函数通过声明它们为参数来请求它们所需要的固定装置。 当pytest去运行测试,它会查看该函数的签名参数,然后搜索与这些参数的名称相同的固定装置。一旦pytest找到了它们,它就会运行这些固定装置,捕获它们返回的内容(如果有的话),并将这些对象作为参数传递给测试函数。
import pytest
class Fruit:
    def __init__(self, name):
        self.name = name
        self.cubed = False

    def cube(self):
        self.cubed = True
class FruitSalad:
    def __init__(self, *fruit_bowl):
        self.fruit = fruit_bowl
        self._cube_fruit()

    def _cube_fruit(self):
        for fruit in self.fruit:
            fruit.cube()

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

def test_fruit_salad(fruit_bowl):
    # Act
    fruit_salad = FruitSalad(*fruit_bowl)
    # Assert
    for fruit in fruit_salad.fruit:
        print(fruit.cubed)
    assert all(fruit.cubed for fruit in fruit_salad.fruit)

不适用固定装置来实现:

import pytest
class Fruit:
    def __init__(self, name):
        self.name = name
        self.cubed = False

    def cube(self):
        self.cubed = True
class FruitSalad:
    def __init__(self, *fruit_bowl):
        self.fruit = fruit_bowl
        self._cube_fruit()

    def _cube_fruit(self):
        for fruit in self.fruit:
            fruit.cube()
            
def fruit_bowl():
    return [Fruit("apple"), Fruit("banana")]
def test_fruit_salad(fruit_bowl):
    # Act
    fruit_salad = FruitSalad(*fruit_bowl)
    # Assert
    assert all(fruit.cubed for fruit in fruit_salad.fruit)
# Arrange
bowl = fruit_bowl()
test_fruit_salad(fruit_bowl=bowl)

  1. 固定装置可以请求调用其他固定装置 pytest最大的优势之一就是灵活的夹具系统,它允许我们将测试的复杂需求归结为更简单和有组织的功能,我们只需要让每个人来描述它们所依赖的东西。示例:
import pytest

# 布置一个夹具
@pytest.fixture
def first_entry():
    return "a"

# 布置第二个夹具
@pytest.fixture
def order(first_entry):
    return [first_entry]

# 调用第二个夹具
def test_string(order):
    # Act
    order.append("b")
    # Assert
    assert order == ["a", "b"]

def test_int(order):
    # Act
    order.append(2)
    # Assert
    assert order == ["a", 2]

  1. 测试用例或者固定装置fixture可以请求多个固定装置 fixture 测试和装置并不限于一次要求一个装置。他们可以要求多少就要求多少
import pytest

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

@pytest.fixture
def second_entry():
    return 2

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

@pytest.fixture
def expected_list():
    return ["a", 2, 3.0]

def test_int(order, expected_list):
    # Act
    order.append(3.0)
    # Assert
    assert order == expected_list

  1. 每次测试可以多次请求固定装置(返回值会被缓存) 在同一测试中也可以多次请求固定装置,并且pytest不会在该测试中再次执行它们,这些装置不会被执行第二次。
import pytest

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

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

# Act
@pytest.fixture
def append_first(order, first_entry):
    return order.append(first_entry)

def test_string_only(append_first, order, first_entry):
    # Assert
    print(append_first)  # None
    print(order)  # ['a'] 这里的order有a,是因为调用了order.append(first_entry),夹具 order 的新值被缓存了
    assert order == [first_entry]

2.3.2 自动固定装置(不需要你请求调用)

有时你可能想要一个固定装置(甚至几个),这些装置是被所有的测试环境依赖,“Autouse”的固定装置是一种方便的方式,使所有的测试自动请求他们。这可以减少大量的冗余请求,甚至可以提供更高级的使用固定装置, 在本例中,append_first固定装置是一种自动固定装置。因为它是自动发生的,所以两个测试都会受到它的影响,即使两个测试都没有要求它。但这并不意味着他们不能被要求;只是这是没有必要的。

import pytest

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

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

# Act
@pytest.fixture(autouse=True)
def append_first(order, first_entry):
    return order.append(first_entry)

def test_string_only(order, first_entry):
    assert order == [first_entry]

def test_string_and_int(order, first_entry):
    order.append(2)
    assert order == [first_entry, 2]

collected 2 items                                                                                                                                                                                                                         
test_append.py::test_string_only PASSED
test_append.py::test_string_and_int PASSED

2.3.3 固定装置的范围:function, class, module, package or session.

需要网络访问的设备依赖于连接,创建通常花费昂贵。扩展前面的示例,我们可以向@pytest添加一个scope=“模块”参数。夹具调用导致smtp_connection夹具函数, 负责创建到已存在的SMTP服务器的连接,每个测试模块只调用一次(默认是每个测试函数调用一次)。 因此,一个测试模块中的多个测试功能将每个接收相同的smtp_connection夹具实例,从而节省时间。作用域的可能值包括:函数、类、模块、程序包、会话。

# content of conftest.py
import pytest
import smtplib
@pytest.fixture(scope="module")
def smtp_connection():
    return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)

#  test_module.py
def test_ehlo(smtp_connection):
    response, msg = smtp_connection.ehlo()
    assert response == 250
    assert b"smtp.gmail.com" in msg
    assert 0 # for demo purposes
def test_noop(smtp_connection):
    response, msg = smtp_connection.noop()
    assert response == 250
    assert 0 # for demo purposes

使用smtp_connection的两个测试函数运行速度一样快,因为它们重用相同的实例。 如果您决定要有一个会话范围内的smtp_connection实例,您可以简单地声明它,这将表明所有的会话将重用相同的实例:

# content of conftest.py
@pytest.fixture(scope="session")
def smtp_connection():
    # 返回的夹具值将被共享给所有请求它的测试
    ...

  1. 固定装置范围(每个目录下都可以添加conftest.py文件) 方法:在默认范围内,固定装置在测试结束时被破坏。默认每个方法都会运行这个夹具 类:设备在拆卸类中的最后一个测试时被破坏。每个类中只运行一次这个固定装置, 模块:在模块中最后一次测试的拆卸期间被破坏。每个py文件中只运行一次这个固定装置,比如py文件总共有10个用例,10个用例执行完了,固定装置结束运行 包:在拆卸包中的最后一次测试时,固定装置被破坏。每个目录下只执行一次这个固定装置,目录下的所有测试用例只执行一次 会话:该设备在测试会话结束时被销毁。运行pytest,发现1000条用例,1000条用例执行前调用一次,只调用一次,结束后停止改固定装置
  2. 动态的范围 在某些情况下,您可能希望更改设备的范围,而不更改代码。要做到这一点,请传递一个可调用到作用域。可调用项必须返回一个具有有效作用域的字符串, 并且在夹具定义期间只执行一次。它将用两个关键字参数来调用——fixture_name作为一个字符串和配置一个配置对象。 这在处理需要时间进行设置的固定装置时特别有用,比如生成一个码头工人容器。您可以使用命令行参数来控制不同环境的衍生容器的范围。请参见下面的示例。
import pytest
def determine_scope(fixture_name, config):
    if config.getoption("--keep-containers", None):
        return "session"
    return "function"
def spawn_container():
    pass
@pytest.fixture(scope=determine_scope)
def docker_container():
    yield spawn_container()

2.3.4 测试后置 teardown/cleanup

当我们运行测试时,我们希望确保它们自己清理,这样它们就不会干扰任何其他测试(这样我们就不会留下大量的测试数据来膨胀系统), teardown系统允许我们定义每个夹具自行清理所需的具体步骤。

  1. yield fixture 固定装置 (推荐) “Yield” 固定装置是 yield 而不是 return,使用这些装置,我们可以运行一些代码,并将一个对象传递回请求的装置/测试,就像使用其他装置一样。唯一的区别是: (1)yield 替代了 return (2)teardown代码被放到了 yield 代码后面 (3)代码yield之前执行出现异常,则不执行yield后代码 (4)一旦最小的计算出了一个装置的线性顺序,它将运行每一个,直到它返回或收益,然后继续到列表中的下一个装置来做同样的事情。 (5)一旦测试完成,pytest将返回到固定装置列表中,但按照相反的顺序,取每个生成的代码,并在其中运行生成语句之后的代码。
# content of emaillib.py
class MailAdminClient:
    def create_user(self):
        return MailUser()
    def delete_user(self, user):
        # do some cleanup
        pass

class MailUser:
    def __init__(self):
        self.inbox = []
    def send_email(self, email, other):
        other.inbox.append(email)
    def clear_mailbox(self):
        self.inbox.clear()
class Email:
    def __init__(self, subject, body):
        self.subject = subject
        self.body = body

# content of test_emaillib.py
import pytest
from emaillib import Email, MailAdminClient
@pytest.fixture
def mail_admin():
    return MailAdminClient()
@pytest.fixture
def sending_user(mail_admin):
    user = mail_admin.create_user()
    yield user
    mail_admin.delete_user(user)
@pytest.fixture
def receiving_user(mail_admin):
    user = mail_admin.create_user()
    yield user
    mail_admin.delete_user(user)


def test_email_received(sending_user, receiving_user):
    email = Email(subject="Hey!", body="How's it going?")
    sending_user.send_email(email, receiving_user)
    assert email in receiving_user.inbox

  1. 直接添加最终分配器 虽然产量固定装置被认为是更干净、更直接的选项,但还有另一种选择,那就是将“终结器”函数直接添加到测试的请求-上下文对象中。它带来了类似的结果,但需要更冗长。 为了使用这种方法,我们必须在夹具中请求请求上下文对象(就像我们请求另一个夹具一样),我们需要为添加拆卸代码,然后将包含拆卸代码的可调用传递给它的附加终结器方法。 但是我们必须小心,因为pytest将在添加后运行该终结器,即使该固定装置在添加终结器后引发一个异常。因此,为了确保我们在不需要的时候不会运行终结器代码,我们只会在设备完成了我们需要拆卸的事情时添加终结器
# content of test_emaillib.py
import pytest
from emaillib import Email, MailAdminClient
@pytest.fixture
def mail_admin():
    return MailAdminClient()
@pytest.fixture
def sending_user(mail_admin):
    user = mail_admin.create_user()
    yield user
    mail_admin.delete_user(user)
@pytest.fixture
def receiving_user(mail_admin, request):
    user = mail_admin.create_user()
    def delete_user():
        mail_admin.delete_user(user)
    request.addfinalizer(delete_user)
    return user
@pytest.fixture
def email(sending_user, receiving_user, request):
    _email = Email(subject="Hey!", body="How's it going?")
    sending_user.send_email(_email, receiving_user)
    def empty_mailbox():
        receiving_user.clear_mailbox()
    request.addfinalizer(empty_mailbox)
    return _email

def test_email_received(receiving_user, email):
    assert email in receiving_user.inbox

2.3.5 安全的 teardown

小测试的固定系统非常强大,但它仍然由电脑运行,所以它不知道如何安全地拆卸我们扔给它的所有东西。如果我们不小心,错误位置的错误可能会影响我们的测试,这可能会很快导致进一步的问题。 安全夹具结构: 最安全和最简单的固定装置结构要求限制每个固定装置只做一个改变状态的动作,然后将它们与它们的拆卸代码捆绑在一起,如上面的电子邮件示例所示。

状态更改操作可能失败但仍然修改状态的可能性可以忽略不计,因为这些操作中的大多数往往是基于事务的(至少在状态可能落后的测试级别上是这样)。 成功的状态改变操作通过将其移动到一个单独的固定函数,并将其与其他可能失败的状态改变操作分开,那么我们的测试将最有可能离开他们发现的测试环境。

from uuid import uuid4
from urllib.parse import urljoin
from selenium.webdriver import Chrome
import pytest
from src.utils.pages import LoginPage, LandingPage
from src.utils import AdminApiClient
from src.utils.data_types import User

@pytest.fixture
def admin_client(base_url, admin_credentials):
    return AdminApiClient(base_url, **admin_credentials)
@pytest.fixture
def user(admin_client):
    _user = User(name="Susan", username=f"testuser-{uuid4()}", password="P4$$word")
    admin_client.create_user(_user)
    yield _user
    admin_client.delete_user(_user)
@pytest.fixture
def driver():
    _driver = Chrome()
    yield _driver
    _driver.quit()
@pytest.fixture
def login(driver, base_url, user):
    driver.get(urljoin(base_url, "/login"))
    page = LoginPage(driver)
    page.login(user)
@pytest.fixture
def landing_page(driver, login):
    return LandingPage(driver)


def test_name_on_landing_page_after_login(landing_page, user):
    assert landing_page.header == f"Welcome, {user.name}!"

2.3.6 安全的运行多个断言语句

请注意,这些方法只是在签名中引用自我作为一种形式。没有任何状态与实际的测试类绑定,因为它可能在 unittest.TestCase 框架。一切都由最精确的夹具系统来管理。

# contents of tests/end_to_end/test_login.py
from uuid import uuid4
from urllib.parse import urljoin
from selenium.webdriver import Chrome
import pytest
from src.utils.pages import LoginPage, LandingPage
from src.utils import AdminApiClient
from src.utils.data_types import User

@pytest.fixture(scope="class")
def admin_client(base_url, admin_credentials):
    return AdminApiClient(base_url, **admin_credentials)

@pytest.fixture(scope="class")
def user(admin_client):
    _user = User(name="Susan", username=f"testuser-{uuid4()}", password="P4$$word")
    admin_client.create_user(_user)
    yield _user
    admin_client.delete_user(_user)
    
@pytest.fixture(scope="class")
def driver():
    _driver = Chrome()
    yield _driver
    _driver.quit()
    
@pytest.fixture(scope="class")
def landing_page(driver, login):
    return LandingPage(driver)


class TestLandingPageSuccess:
    @pytest.fixture(scope="class", autouse=True)
    def login(self, driver, base_url, user):
        driver.get(urljoin(base_url, "/login"))
        page = LoginPage(driver)
        page.login(user)
    
    def test_name_in_header(self, landing_page, user):
        assert landing_page.header == f"Welcome, {user.name}!"
        
    def test_sign_out_button(self, landing_page):
        assert landing_page.sign_out_button.is_displayed()
        
    def test_profile_link(self, landing_page, user):
        profile_href = urljoin(base_url, f"/profile?id={user.profile_id}")
        assert landing_page.profile_link.get_attribute("href") == profile_href

登录固定装置也是在类中定义的,因为并不是模块中的所有其他测试都期望成功登录,并且可能需要对另一个测试类进行一些不同的处理。例如,如果我们想编写另一个测试场景,我们可以通过在测试文件中添加类似的内容来处理它:

class TestLandingPageBadCredentials:
    @pytest.fixture(scope="class")
    def faux_user(self, user):
        _user = deepcopy(user)
        _user.password = "badpass"
        return _user

    def test_raises_bad_credentials_exception(self, login_page, faux_user):
        with pytest.raises(BadCredentialsException):
            login_page.login(faux_user)

2.3.7 固定装置可以处理请求的内容【看不懂】

固定函数可以接受请求对象,以介绍“请求”测试函数、类或模块上下文。进一步扩展之前的smtp_connection夹具示例,让我们从使用我们的夹具的测试模块中读取一个可选的服务器URL:

# content of conftest.py
import pytest
import smtplib
@pytest.fixture(scope="module")
def smtp_connection(request):
    server = getattr(request.module, "smtpserver", "smtp.gmail.com")
    smtp_connection = smtplib.SMTP(server, 587, timeout=5)
    yield smtp_connection
    print("finalizing {} ({})".format(smtp_connection, server))
    smtp_connection.close()

# content of test_anothersmtp.py
smtpserver = "mail.python.org" # will be read by smtp fixture
def test_showhelo(smtp_connection):
    assert 0, smtp_connection.helo()

smtp_connection 固定装置从模块名称空间中获取了我们的邮件服务器名称:pytest -qq --tb=short test_anothersmtp.py

🎁更多干货


完整版文档下载方式:

这些资料,对于从事【软件测试】等相关工作的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享。

在评论区和我互动交流或者私❤我【软件测试学习】领取即可,拿走不谢。


如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “👍点赞” “✍️评论” “💙收藏” 一键三连哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/687200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis列表类型(list)模拟队列操作

文章目录 Redis列表类型模拟队列操作1. 使用用lpush和rpop模拟队列的操作1.1 lpush介绍1.2 rpop介绍1.3 llen介绍1.4 lrange介绍1.5 del命令介绍 2. 使用用rpush和lpop模拟队列的操作2.1 rpush介绍2.2 lpop介绍 Redis列表类型模拟队列操作 Redis的列表类型(list&…

JavaScript 中内存泄漏的几种情况?

一、是什么 内存泄漏(Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存 并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失…

emoji表情符号,unicode表情符号

目录 😎前言👨‍💻使用✈️Unicode 1.1 版本新增☙ Unicode 3.0 版本新增♻️Unicode 3.2 版本新增☕Unicode 4.0 版本新增🀨Unicode 5.1 版本新增⚽ Unicode 5.2 版本新增🌊Unicode 6.0 版本新增😙Unicode…

集合专题----List篇

1、Collection常用方法 package com.example.collection.Collection;import java.util.ArrayList; import java.util.List;public class Collection03 {public static void main(String[] args) {List list new ArrayList();//接口可以指向实现该接口的类//add:添加单个元素l…

BEVFromer论文研读

1. 总体结构 上图为BEVFormer在t时刻的网络结构。图(a) 表示的是BEVFormer的encoder层。BEVFormer有6个encoder层,每一个encoder除了本文自定义的三个组件外都和传统的transformers结果一致。自定义的三个组件分别是网格状的BEV queries,TSA和SCA。其中B…

【智慧交通项目实战】 《 OCR车牌检测与识别》(三):基于改进CRNN的车牌识别

👨‍💻作者简介: CSDN、阿里云人工智能领域博客专家,新星计划计算机视觉导师,百度飞桨PPDE,专注大数据与AI知识分享。✨公众号:GoAI的学习小屋 ,免费分享书籍、简历、导图等&#xf…

接口测试实战篇,吐血整理汇总,接口测试你知多少?

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 接口测试&#xf…

网络安全应急响应最全教学从懵懂—入门—精通(2023年6月)

目录 0、写在前面1、概念及应急响应流程2、Windows排查2.1文件排查2.2、进程排查2.3、系统信息排查2.4、工具排查2.5、日志排查 3、Linux排查3.1、文件排查3.2、进程排查3.3、系统信息排查3.4、后门排查3.5、日志排查3.5.1、基于时间的日志管理3.5.2、系统日志管理3.5.3、中间件…

计算机毕业论文内容参考|基于网络的打印机数据传输系统的设计与实现

文章目录 导文前言绪论课题背景国内外现状与趋势课题内容相关技术与方法介绍系统分析系统设计系统实现系统测试总结与展望导文 计算机毕业论文内容参考|基于网络的打印机数据传输系统的设计与实现 前言 打印机是现代办公和生活中不可或缺的设备之一,随着信息化和网络化的发展…

常用激活函数及偏导

常用激活函数及偏导 derivative.py import numpy as np import matplotlib.pyplot as pltplt.subplots_adjust(hspace0.5 , wspace0.5) rows 3 cols 2def plot_style(ax):# 设置轴隐藏ax.spines[top].set_visible(False) ax.spines[right].set_visible(False)# 设置轴位置ax.…

python 创建二维列表【空列表或定长列表】

正确写法: 空列表设置了初始值的定长列表 ### 创建3行的二维空列表 ### a [[] for i in range(3)] print(a) # [[], [], []]### 每一行非空,并设定初始值及其长度 ### ### 创建3行,每一行列表初始化全0的列表,且长度为5 ### a…

Redis 实现库存扣减

在日常开发中有很多地方都有类似扣减库存的操作,比如电商系统中的商品库存,抽奖系统中的奖品库存等。这次分享的是库存的操作怎么防止商品被超卖。 解决方案 基于数据库单库存基于数据库多库存基于redis 解决思路 使用mysql数据库,使用一个…

[架构之路-216]- 架构 - 概念架构 - 模块(Module)、组件(Component)、包(Package)、对象、函数的区别

前言: 在软件架构中,一个重要的任务就是切分系统,而切分系统进程涉及到一个基本的概念,如模块(Module)、组件(Component)、包(Package)、对象,本…

亚马逊云科技以用户为中心,持续丰富安全服务和解决方案

AI加持安全,自动化运营成未来趋势 亚马逊云科技始终在云安全领域不断创新探索、深耕发力,随着全球技术的发展而持续迭代安全能力。 当下,以ChatGPT为代表的AIGC成为最出圈的热点,大量的科技巨头纷纷涌入AI赛道,投入了…

16. WebGPU 数据内存布局

在 WebGPU 中,提供给它的几乎所有数据都需要在内存中设定布局,以匹配在着色器中定义的内容。这与 JavaScript 和 TypeScript 形成鲜明对比,后者很少出现内存布局问题。 在 WGSL 中,当编写着色器时,通常定义 struct 。…

Redis集群详解

目录 一、前言二、Redis 集群方案应该怎么做?都有哪些方案?三、twemproxy四、分而治之-codis1、codis简介2、Codis 分片原理3、不同的 Codis 实例之间槽位关系如何同步?4、假如Redis扩容,如何调整槽位的?5、codis优缺点…

巧用 overflow-scroll 实现丝滑轮播图

前言: 近期我在项目中就接到了一个完成轮播图组件的需求。最开始我也像大家一样,直接选择使用了知名的开源项目 “Swiper”,但是后来发现它在移动端项目中某些测试环境下会白屏一段时间。无论如何调试都不能修复这个问题,于是就自己上手写了个…

Java智慧工厂UWB高精度人员定位管理系统源码

一、系统概述: 智慧工厂高精度定位管理系统源码,采用UWB定位技术,通过部署UWB定位设备实现人、车、物精确定位,打造可寻、可视、可防、可控的一体化管控平台。UWB定位系统具有容量大、稳定性强、精度高、安装便捷、易维护、操作简…

pebblely无需代码连接集简云数据表的方法

1 使用场景 当电商UI接到一个设计产品图的需求时,按照常规的工作安排,从对接需求到最后完成效果图最短时间都要在5天左右,如果遇到高要求的客户,后期还需要在电脑上进一步调整细节,一张成片起码要花上数小时时间去完成…

LabVIEW开发压电陶瓷特性测试系统

LabVIEW开发压电陶瓷特性测试系统 目前,随着科学技术的飞速发展,各个领域都需要纳米级的精密定位系统。而压电定位系统是迄今为止已经得到认可的微纳定位系统。该系统主要由压电驱动系统、位移测量传感器和控制系统组成。其中,整个压电驱动系…