范围为“class”的 Pytest 装置不适用于“setup_class”方法

Raf*_* C. 5 python pytest python-3.x

我目前正在用来pytest_addoption运行 API 测试,因此测试应该针对用户在命令行上使用的环境运行。在我的测试文件中,我尝试UsersSupport仅实例化该类一次,并传递env参数。我的代码:

测试.py

import pytest

# Environments
QA1 = 'https://qa1.company.com'
LOCALHOST = 'https://localhost'


def pytest_addoption(parser):
    parser.addoption(
        '--env',
        action='store',
        default='qa1'
    )


@pytest.fixture(scope='class')
def env(request):
    cmd_option = request.config.getoption('env')
    if cmd_option == 'qa1':
        chosen_env = QA1
    elif cmd_option == 'localhost':
        chosen_env = LOCALHOST
    else:
        raise UnboundLocalError('"--env" command line must use "qa1", "localhost"')
    return chosen_env
Run Code Online (Sandbox Code Playgroud)

用户支持.py

import requests


class UsersSupport:

    def __init__(self, env):
        self.env = env
        self.users_endpoint = '/api/v1/users'

    def create_user(self, payload):
        response = requests.post(
            url=f'{self.env}{self.users_endpoint}',
            json=payload,
        )
        return response
Run Code Online (Sandbox Code Playgroud)

post_create_user_test.py

import pytest
from faker import Faker
from projects import UsersSupport
from projects import users_payload


class TestCreateUser:

    @pytest.fixture(autouse=True, scope='class')
    def setup_class(self, env):
        self.users_support = UsersSupport(env)
        self.fake = Faker()
        self.create_user_payload = users_payload.create_user_payload

    def test_create_user(self):
        created_user_res = self.users_support.create_user(
            payload=self.create_user_payload
        ).json()
        print(created_user_res)
Run Code Online (Sandbox Code Playgroud)

问题

当我运行时pytest projects/tests/post_create_user_test.py --env qa1出现AttributeError: 'TestCreateUser' object has no attribute 'users_support'错误,但是如果我删除scopefromsetup_class方法,则该方法将在每个方法上运行,而不是在所有方法上运行。

如何env在 中使用固定装置setup_class并实例化该类UsersSupport以在所有方法中使用?

MrB*_*men 10

如果您使用具有类范围的固定装置,则该self参数不会引用类实例。但是,您仍然可以使用 访问类本身self.__class__,因此您可以从实例变量创建类变量。

您的代码可能如下所示:

import pytest
from faker import Faker
from projects import UsersSupport
from projects import users_payload


class TestCreateUser:
   
    @pytest.fixture(autouse=True, scope='class')
    def setup_class(self, env):
        self.__class__.users_support = UsersSupport(env)
        self.__class__.fake = Faker()
        self.__class__.create_user_payload = users_payload.create_user_payload

    def test_create_user(self):
        created_user_res = self.users_support.create_user(
            payload=self.create_user_payload
        ).json()  # now you access the class variable
        print(created_user_res)
Run Code Online (Sandbox Code Playgroud)

在测试过程中,为每个测试创建一个新的测试实例。
如果您有一个默认函数范围的固定装置,它将在测试的同一实例中调用,以便self固定装置和当前测试的参数引用相同的实例。

对于类范围的固定装置,设置代码在创建测试实例之前在单独的实例中运行 - 该实例必须存活到所有测试结束才能执行拆卸代码,因此它与所有测试实例不同测试实例。由于它仍然是同一测试类的实例,因此在这种情况下您可以将变量存储在测试类中。

您还可以将类范围的固定装置设为类方法,因为无论如何您只需要访问该类:

class TestCreateUser:
   
    @pytest.fixture(autouse=True, scope='class')
    @classmethod
    def setup_class(cls, env):
        cls.users_support = UsersSupport(env)
        cls.fake = Faker()
        cls.create_user_payload = users_payload.create_user_payload
Run Code Online (Sandbox Code Playgroud)

这使得意图更清晰,代码更具可读性。请注意,在这种情况下,您必须将类方法装饰器放在固定装置装饰器之后。