簡體   English   中英

如何在測試之間共享全局變量?

[英]How to share global variables between tests?

我在 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)

但這並不像我預期的那樣工作,因為所有導入都在夾具之前執行,並且 test_foo import api_url = Nonecmd_param 夾具重新定義它之前。 然后我編寫 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 選項而不更改測試文件?

根據docspytest_namespace已在 4.0 版中刪除:

可以使用pytest_configure來共享全局變量。

示例:

import pytest

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

注意: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

我不會弄亂全局變量。 只需定義您的夾具以返回一個值並在您的測試中使用該夾具:類似於@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

我只是嘗試在不完全更改您的代碼的情況下使其工作。 我希望它能給你一些想法。

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 請注意,在api_url從夾具conftest.py在夾具數據使用

import requests

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

test_bar(data):
    assert data is not None

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"]

我在 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

這將適用於同一模塊中的所有測試。 如果您對跨測試模塊使用相同的“存儲”感興趣,請使用字典或命名元組而不是我使用的類。 為了確保在某些測試失敗時不會出現缺失值錯誤,請使用 None 初始化所有已知值。

您目前可以按照文檔中的說明直接使用 pytest 對象,但只能作為權宜之計

import pytest


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

但請注意是否使用pytest_namespace版本,因為它已被棄用:

使用命名空間的舊版本:

class MySymbol:
    ...


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

只是想分享最適合我的模式,我覺得它簡單明了。

conftest.py我有:

import pytest
from types import SimpleNameSpace


# Temporary Storage

pytest.tmp = SimpleNameSpace()

@pytest.fixture(autouse=True, scope="class")
def set_tmp_cls():
    pytest.tmp.cls = SimpleNameSpace()

@pytest.fixture(autouse=True, scope="module")
def set_tmp_mod():
    pytest.tmp.mod = SimpleNameSpace()

然后在測試中使用時:

import pytest
from pytest import tmp

def test_foo():
    tmp.cls.var = 1

我使用了這個 class。 真正簡單且可擴展以供以后的變量使用

class TestVars:
    """Class to save and use variables"""

    def __init__(self) -> None:
        self.variables = {}

    def save_var(self, variable: dict):
        self.variables.update(variable)

    def get_var(self, key):
        return self.variables[key]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM