【pytest】fixture参考
【摘要】 一、什么是固定装置夹具 为测试提供了定义的、可靠的和一致的上下文夹具定义构成测试的安排阶段的步骤和数据测试函数通过参数访问由固定装置设置的服务、状态或其他操作环境。import pytestclass Fruit: def __init__(self, name): self.name = name def __eq__(self, other): r...
一、什么是固定装置
夹具 为测试提供了定义的、可靠的和一致的上下文
夹具定义构成测试的安排阶段的步骤和数据
测试函数通过参数访问由固定装置设置的服务、状态或其他操作环境。
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
二、“请求”赛程
在基本层面上,测试函数通过将它们声明为参数来请求它们所需的装置。
当 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()
# Arrange
@pytest.fixture
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)
三、夹具使用
pytest 的最大优势之一是其极其灵活的夹具系统。它使我们能够将复杂的测试需求分解为更简单、更有组织的功能,我们只需要让每个功能描述它们所依赖的东西。
- 灯具可以请求其他灯具
-
# contents of test_append.py import pytest # Arrange @pytest.fixture def first_entry(): return "a" # Arrange @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]
- 夹具可重复使用
-
# contents of test_append.py import pytest # Arrange @pytest.fixture def first_entry(): return "a" # Arrange @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]
-
一个测试/夹具一次可以请求多个夹具
-
# contents of test_append.py import pytest # Arrange @pytest.fixture def first_entry(): return "a" # Arrange @pytest.fixture def second_entry(): return 2 # Arrange @pytest.fixture def order(first_entry, second_entry): return [first_entry, second_entry] # Arrange @pytest.fixture def expected_list(): return ["a", 2, 3.0] def test_string(order, expected_list): # Act order.append(3.0) # Assert assert order == expected_list
-
自动使用装置
-
# contents of test_append.py import pytest @pytest.fixture def first_entry(): return "a" @pytest.fixture def order(first_entry): return [] @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]
-
范围:跨类、模块、包或会话共享固定装置
夹具在测试首次请求时创建,并根据其销毁scope
:
-
-
function
:默认范围,测试结束时夹具被销毁。 -
class
:在类中最后一个测试的拆卸过程中,夹具被破坏。 -
module
:在模块中最后一次测试的拆卸过程中,夹具被破坏。 -
package
:在定义夹具的包(包括其中的子包和子目录)中最后一个测试的拆卸过程中,夹具被破坏。 -
session
:夹具在测试结束时被破坏。
-
-
动态范围
-
def determine_scope(fixture_name, config): if config.getoption("--keep-containers", None): return "session" return "function" @pytest.fixture(scope=determine_scope) def docker_container(): yield spawn_container()
-
拆卸/清理(又名夹具完成)
-
1.
yield
固定装置(推荐) -
2. 直接添加终结器(终结器按照先进后出的顺序执行)
-
@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
-
3.安全拆解
-
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}!"
-
安全地运行多个
assert
语句 -
# 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
-
夹具可以内省请求的测试上下文
-
工厂作为固定装置
-
参数化夹具
-
# content of conftest.py import smtplib import pytest @pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"]) def smtp_connection(request): smtp_connection = smtplib.SMTP(request.param, 587, timeout=5) yield smtp_connection print(f"finalizing {smtp_connection}") smtp_connection.close()
-
模块化:使用固定功能中的固定装置
-
# content of test_appsetup.py import pytest class App: def __init__(self, smtp_connection): self.smtp_connection = smtp_connection @pytest.fixture(scope="module") def app(smtp_connection): return App(smtp_connection) def test_smtp_connection_exists(app): assert app.smtp_connection
-
在类和模块中使用固定装置
usefixtures
-
# content of conftest.py import os import tempfile import pytest @pytest.fixture def cleandir(): with tempfile.TemporaryDirectory() as newpath: old_cwd = os.getcwd() os.chdir(newpath) yield os.chdir(old_cwd) # content of test_setenv.py import os import pytest @pytest.mark.usefixtures("cleandir") class TestDirectoryInit: def test_cwd_starts_empty(self): assert os.listdir(os.getcwd()) == [] with open("myfile", "w", encoding="utf-8") as f: f.write("hello") def test_cwd_again_starts_empty(self): assert os.listdir(os.getcwd()) == []
-
使用直接测试参数化覆盖夹具
-
tests/ conftest.py # content of tests/conftest.py import pytest @pytest.fixture def username(): return 'username' @pytest.fixture def other_username(username): return 'other-' + username test_something.py # content of tests/test_something.py import pytest @pytest.mark.parametrize('username', ['directly-overridden-username']) def test_username(username): assert username == 'directly-overridden-username' @pytest.mark.parametrize('username', ['directly-overridden-username-other']) def test_username_other(other_username): assert other_username == 'other-directly-overridden-username-other'
-
用非参数化夹具覆盖参数化夹具,反之亦然
-
tests/ conftest.py # content of tests/conftest.py import pytest @pytest.fixture(params=['one', 'two', 'three']) def parametrized_username(request): return request.param @pytest.fixture def non_parametrized_username(request): return 'username' test_something.py # content of tests/test_something.py import pytest @pytest.fixture def parametrized_username(): return 'overridden-username' @pytest.fixture(params=['one', 'two', 'three']) def non_parametrized_username(request): return request.param def test_username(parametrized_username): assert parametrized_username == 'overridden-username' def test_parametrized_username(non_parametrized_username): assert non_parametrized_username in ['one', 'two', 'three'] test_something_else.py # content of tests/test_something_else.py def test_username(parametrized_username): assert parametrized_username in ['one', 'two', 'three'] def test_username(non_parametrized_username): assert non_parametrized_username == 'username'
-
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)