在 class 中的所有测试之前,Pytest "run-around-tests" 固定到 运行 一次

Pytest "run-around-tests" fixture to run only once before all tests in a class

我正在使用 pytest + selenium 测试 Web 解决方案的用户消息功能。测试将为测试用户生成一条测试消息,然后登录该用户以验证该消息确实正在为该用户显示。

所以测试场景基本是:

  1. 在测试启动时,通过 API 辅助函数生成一个新的 AUTH 令牌。
  2. 向另一个API发送请求以设置新消息(需要AUTH令牌)
  3. 向另一个 API 发送请求以 "map" 将此消息发送给指定用户(需要 AUTH 令牌)
  4. 登录测试用户并验证新消息确实在显示。

我的问题是我想避免每次测试中的每个测试都创建一个新的 AUTH 令牌 class 是 运行 - 我想创建一个新的令牌,一旦所有测试都在其中使用同样的测试运行。

在调用所有测试时生成一个新的访问令牌的最聪明的解决方案是什么?

现在我想出了这样的东西,每次任何单独的测试都会生成一个新的令牌 运行:

import pytest
import helpers.api_access_token_helper as token_helper
import helpers.user_message_generator_api_helper as message_helper
import helpers.login_helper as login_helper
import helpers.popup_helper as popup_helper

class TestStuff(object):

    @pytest.yield_fixture(autouse=True)
    def run_around_tests(self):
        yield token_helper.get_api_access_token()

    def test_one(self, run_around_tests):
        auth_token = run_around_tests
        message_helper.create_new_message(auth_token, some_message_data)
        message_helper.map_message_to_user(auth_token, user_one["user_id"])
        login_helper.log_in_user(user_one["user_name"], user_one["user_password"])
        assert popup_helper.user_message_is_displaying(some_message_data["title"])

    def test_two(self, run_around_tests):
        auth_token = run_around_tests
        message_helper.create_new_message(auth_token, some_other_message_data)
        message_helper.map_message_to_user(auth_token, user_two["user_id"])
        login_helper.log_in_user(user_two["user_name"], user_two["user_password"])
        assert popup_helper.user_message_is_displaying(some_other_message_data["title"])

我在 "run-around-tests" 夹具上来回反复尝试,但未能找到解决方案。

您必须调整夹具作用域以缓存测试 运行 (scope='session') 中的所有测试、模块 (scope='module') 中的所有测试、[ 中的所有测试的结果=42=](仅限旧的 unittest 样式测试,scope='class'),或用于单个测试(scope='function';这是默认测试)。示例:

夹具功能

@pytest.fixture(scope='session')
def token():
    return token_helper.get_api_access_token()


class Tests(object):

    def test_one(self, token):
        ...

    def test_two(self, token):
        ...


class OtherTests(object):

    def test_one(self, token):
        ...

令牌将在第一次请求时计算一次并在整个测试过程中保存在缓存中运行,因此所有三个测试Tests::test_oneTests::test_twoOtherTests::test_one将共享相同的令牌值。

夹具class方法

如果您打算编写旧式测试 classes 而不是测试函数,并且希望夹具成为 class 方法(就像在您的代码中一样),请注意您可以仅使用 class 作用域,因此夹具值仅在 class:

中的测试之间共享
class TestStuff(object):

    @pytest.fixture(scope='class')
    def token(self):
        return token_helper.get_api_access_token()

    def test_one(self, token):
        ...

    def test_two(self, token):
        ...

暂且不论:

  1. pytest.yield_fixture 已弃用并替换为 pytest.fixture;
  2. 您不需要设置 autouse=True 因为您在测试参数中明确请求了夹具。无论如何都会被调用。

您可以将 scope="module" 参数添加到 @pytest.fixture。

根据pytest documentation

Fixtures requiring network access depend on connectivity and are usually time-expensive to create. Extending the previous example, we can add a scope="module" parameter to the @pytest.fixture invocation to cause a smtp_connection fixture function, responsible to create a connection to a preexisting SMTP server, to only be invoked once per test module (the default is to invoke once per test function). Multiple test functions in a test module will thus each receive the same smtp_connection fixture instance, thus saving time. Possible values for scope are: function, class, module, package or session.

# content of conftest.py
import pytest
import smtplib


@pytest.fixture(scope="module")
def smtp_connection():
    return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)

夹具范围

夹具在测试首次请求时创建,并根据其范围销毁:*

function:默认作用域,fixture在测试结束时销毁。

class: 在 class.

的最后一个测试的拆解过程中 fixture 被破坏了

module: 夹具在模块中最后一个测试的拆卸过程中被破坏。

package: fixture 在包中最后一个测试的拆卸过程中被破坏。

session: fixture 在测试会话结束时销毁。

注意:Pytest 一次只缓存一个夹具实例,这意味着当使用参数化夹具时,pytest 可能会在给定范围内多次调用夹具。