pytest:如何在测试之间共享全局变量(依赖于fixture)

时间:2017-06-08 17:14:21

标签: python pytest

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

conftest.py

api_version = 'v25'
api_url = 'http://www.foobar.com/' + api_version

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

现在我希望能够从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)

但这并不像我预期的那样有效,因为所有导入都在fixtures之前执行,而test_foo导入 api_url =无 之前 cmd_param fixture重新定义了这个。 然后我写了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

但现在我也被迫改变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

它有效,但解决方案看起来很麻烦。有没有更优雅的方法来使用自定义cmd选项而不更改测试文件?

7 个答案:

答案 0 :(得分:5)

注意:现在不推荐使用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

答案 1 :(得分:3)

我只是尝试在不完全更改代码的情况下使其工作。我希望它可以给你一些想法。

在conftest.py中

api_url_by_option = 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_by_option
    if api_version in ['v24', 'v25', 'v26', 'v27']:
        api_url_by_option = 'http://www.foobar.com/' + api_version
    else:
        raise ValueError('Unknown api version: ' + api_version)

@pytest.fixture:
def api_url():
    return api_url_by_option
在test_foo.py中

,您不需要导入api_url。请注意,conftest.py中的api_url fixture用于夹具数据

import requests

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

test_bar(data):
    assert data is not None

答案 2 :(得分:2)

我不会搞乱全局变量。 只需定义夹具以返回值并在测试中使用该夹具: 与@milo发布的相似,但更简单。

您还定义了--api_version CLI选项,但访问了灯具中的--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)

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()

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

答案 3 :(得分:2)

根据docspytest_namespace已在4.0版中删除:

一个人可以使用pytest_configure来共享全局变量。

示例:

import pytest

def pytest_configure():
    pytest.my_symbol = MySymbol()

答案 4 :(得分:0)

我在conftest.py中做什么:


class StoreStuffHere:
    something_to_start_with = "value"
    somethingnew = None

#if you want an empty class:

class StoreStuffHere:
   pass

我在test_sample.py中做什么:

from conftest import StoreStuffHere

store_stuff_here = StoreStuffHere

#this will pass
def test_assert_value_stored():
    store_stuff_here.somethingnew = 45
    assert store_stuff_here.something_to_start_with == "value"

#this will pass
def test_assert_fresh_stored_value():
    assert store_stuff_here.somethingnew == 45

这将适用于同一模块中的所有测试。 如果您对跨测试模块使用相同的“存储”感兴趣,请使用字典代替或使用命名的tupple代替我使用的类。为了确保在某些测试失败时不会出现缺失值错误,请使用无初始化所有已知值。

答案 5 :(得分:0)

您目前可以按照文档中的说明直接使用pytest对象,但只能使用As a stopgap measure

https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace

import pytest


def pytest_configure():
    pytest.my_symbol = MySymbol()

但是请注意,如果不赞成使用namespace版本,请注意: https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace

使用命名空间的旧版本:

class MySymbol:
    ...


def pytest_namespace():
    return {"my_symbol": MySymbol()}

答案 6 :(得分:0)

conftest.py 文件用作为整个目录提供设备的一种方式。在 conftest.py 中定义的 Fixture 可以被该包中的任何测试使用而无需导入它们(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"]