如何在测试之间共享全局变量?

And*_*kov 6 python pytest

我在conftest.py中有一个全局变量,并在测试中使用它。例如:

conftest.py

api_version = 'v25'
api_url = 'http://www.foobar.com/' + api_version
Run Code Online (Sandbox Code Playgroud)

test_foo.py

from conftest import api_url
import requests

@pytest.fixture
def data():
    return requests.request("GET", api_url)

test_bar(data):
    assert data is not None
Run Code Online (Sandbox Code Playgroud)

现在,我希望能够从cmd更改api_version,以测试其他api版本。因此,我通过以下方式修改了conftest.py:

conftest.py

api_url = None

def pytest_addoption(parser):
    parser.addoption("--api_version", action="store", default="v25", help="By default: v25")

@pytest.fixture(autouse=True, scope='session')
def cmd_param(pytestconfig):
    api_version = pytestconfig.getoption("--mobile_api_ver").lower()
    global api_url
    if api_version in ['v24', 'v25', 'v26', 'v27']:
        api_url = 'http://www.foobar.com/' + api_version
    else:
        raise ValueError('Unknown api version: ' + api_version)
Run Code Online (Sandbox Code Playgroud)

但这无法按我预期的那样工作,因为所有导入 Fixtures 之前执行,而test_foo import api_url = cmd_param Fixtures重新定义此值之前执行None。然后,我编写get_api_url方法并从测试模块调用它:

conftest.py

api_url = None

def pytest_addoption(parser):
    parser.addoption("--api_version", action="store", default="v25", help="By default: v25")

@pytest.fixture(autouse=True, scope='session')
def cmd_param(pytestconfig):
    api_version = pytestconfig.getoption("--mobile_api_ver").lower()
    global api_url
    if api_version in ['v24', 'v25', 'v26', 'v27']:
        api_url = 'http://www.foobar.com/' + api_version
    else:
        raise ValueError('Unknown api version: ' + api_version)

def get_api_url():
    return api_url
Run Code Online (Sandbox Code Playgroud)

但是现在我也不得不更改test_foo.py:

test_foo.py

from conftest import get_api_url
import requests

@pytest.fixture
def data():

    return requests.request("GET", get_api_url())

test_bar(data):
    assert data is not None
Run Code Online (Sandbox Code Playgroud)

它可以工作,但是解决方案看起来很尴尬。有没有更优雅的方式来使用自定义cmd选项而不更改测试文件?

yas*_*i94 21

根据docspytest_namespace已在 4.0 版中删除:

可以使用pytest_configure共享全局变量。

例子:

import pytest

def pytest_configure():
    pytest.my_symbol = MySymbol()
Run Code Online (Sandbox Code Playgroud)

  • 如何从代码的其余部分访问 my_symbol? (6认同)
  • 如果有人正在搜索,这个解决方案就有效。至少对我来说:D (5认同)
  • @user613,检查接受的答案。您可以直接使用: pytest.<your_global_variable_name> (3认同)
  • 当您调用固定装置时,代码就会运行。这意味着,在许多情况下,您将多次运行代码。例如,当您使用基于会话的库(如 Spark)时,每次调用固定装置时都会创建一个 Spark 会话。单元测试将花费大量时间才能完成......这不是您想要的。 (2认同)
  • 对我来说,它的工作原理是将这个答案的代码放在名为“conftest.py”的文件中 (2认同)

Dmi*_*rev 7

我不会弄乱全局变量。只需定义您的夹具以返回一个值并在您的测试中使用该夹具:类似于@milo 发布的内容,但要简单得多。

您还定义了--api_versionCLI 选项,但--mobile_api_ver在您的装置中访问选项。此外,您的测试只是检查响应对象是否不是 None,它永远不会是 None,因此即使响应为 404 状态,assert 语句也将始终通过,请参阅内联注释。

这是一些可以工作的代码:

conftest.py 的内容

import pytest


def pytest_addoption(parser):
    parser.addoption("--api_version", action="store", default="v25", help="By default: v25")


@pytest.fixture(scope='session')
def api_url(pytestconfig):
    api_version = pytestconfig.getoption("--api_version").lower()
    if api_version in ['v24', 'v25', 'v26', 'v27']:
        return 'http://www.foobar.com/' + api_version
    else:
        raise ValueError('Unknown api version: ' + api_version)
Run Code Online (Sandbox Code Playgroud)

test_foo.py 的内容

import pytest
import requests


@pytest.fixture
def data(api_url):  # probably a good idea to rename your fixture to a api_response or change what fixture returns.
    return requests.get(api_url)


def test_bar(data):
    print(data.text)
    # below you are not testing data, but merely checking that response object is not None
    assert data is not None  # this will always pass

    # you probably want to test status code and response content
    assert data.status_code == 200
    assert data.json()
Run Code Online (Sandbox Code Playgroud)

运行测试: pytest -vvv --api_version v24 test_foo.py


sup*_*aze 6

注意:pytest_namespace现在已弃用

pytest提供了一种在会话中使用某些全局变量的方法。这些变量也可以被灯具使用。

这些变量通过pytest挂钩进行控制。

import pytest

def pytest_namespace():
    return {'my_global_variable': 0}

@pytest.fixture
def data():
    pytest.my_global_variable = 100

def test(data):
    print pytest.my_global_variable
Run Code Online (Sandbox Code Playgroud)

  • 文档说到`pytest_namespace()`:“(**不推荐使用**)这个钩子导致直接在pytest上进行monkeypatching,强烈建议不要使用它。 (2认同)
  • 看起来最近发生了弃用 (2认同)

Joe*_*icz 5

您当前可以按照文档中所述直接使用 pytest 对象,但只能作为权宜之计

import pytest


def pytest_configure():
    pytest.my_symbol = MySymbol()
Run Code Online (Sandbox Code Playgroud)

但如果使用该pytest_namespace版本请注意,因为它已被弃用:

旧版本使用命名空间:

class MySymbol:
    ...


def pytest_namespace():
    return {"my_symbol": MySymbol()}
Run Code Online (Sandbox Code Playgroud)


小智 5

conftest.py 文件用作为整个目录提供固定装置的方法。conftest.py 中定义的夹具可供该包中的任何测试使用,无需导入它们(pytest 将自动发现它们)。

https://docs.pytest.org/en/6.2.x/fixture.html#conftest-py-sharing-fixtures-across-multiple-files

tests/
__init__.py

conftest.py
    # content of tests/conftest.py
    import pytest

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

    @pytest.fixture
    def top(order, innermost):
        order.append("top")

test_top.py
    # content of tests/test_top.py
    import pytest

    @pytest.fixture
    def innermost(order):
        order.append("innermost top")

    def test_order(order, top):
        assert order == ["innermost top", "top"]

subpackage/
    __init__.py

    conftest.py
        # content of tests/subpackage/conftest.py
        import pytest

        @pytest.fixture
        def mid(order):
            order.append("mid subpackage")

    test_subpackage.py
        # content of tests/subpackage/test_subpackage.py
        import pytest

        @pytest.fixture
        def innermost(order, mid):
            order.append("innermost subpackage")

        def test_order(order, top):
            assert order == ["mid subpackage", "innermost subpackage", "top"]
Run Code Online (Sandbox Code Playgroud)