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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/44441929/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 00:01:35  来源:igfitidea点击:

How to share global variables between tests?

pythonpytest

提问by Andrey Glazkov

I have a global variable in conftest.py and use it in tests. For example:

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

conftest.py

conftest.py

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

test_foo.py

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


Now I want to be able to change api_version from cmd for testing other api version. So I modified conftest.py in the following way:

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

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)

But this doesn't work as I expected because all imports execute before fixtures and test_foo import api_url = Nonebeforecmd_param fixture redefines this. Then i write get_api_url method and call it from test module:

但这并不像我预期的那样工作,因为cmd_param 夹具重新定义它之前,所有导入都在夹具和 test_foo import api_url = None之前执行。然后我编写 get_api_url 方法并从测试模块调用它:

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)

def get_api_url():
    return api_url

But now I was forced to change test_foo.py too:

但现在我也被迫更改 test_foo.py:

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

It works, but solution looks awkward. Is there a more elegant way to use custom cmd options without changing test files?

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

回答by supamaze

Note: pytest_namespace is deprecated now

注意:pytest_namespace 现已弃用

pytest provides a way to use some global variables within the session. These variables can be used by fixtures as well.

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

These variables are controlled via pytest hooks.

这些变量是通过 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

回答by yashtodi94

According to docs, pytest_namespacehas been removed in version 4.0:

根据docspytest_namespace已在 4.0 版中删除:

One can use pytest_configureto share global variables.

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

Example:

例子:

import pytest

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

回答by Dmitry Tokarev

I wouldn't mess with global variables. Just define your fixture to return a value and use that fixture in your tests: Similar to what @milo posted but a lot simpler.

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

Also you had defined --api_versionCLI option but accessing --mobile_api_veroption in your fixture. Additionally your test is just checking that a response object is not None which will never be None, so assert statement will always pass even if response is 404 status, see inline comments.

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

Here is some code that will work:

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

contents of conftest.py

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)

contents of test_foo.py

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 the tests: pytest -vvv --api_version v24 test_foo.py

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

回答by milo

I just try to get it work without complete changing your code. I hope it could give you some idea.

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

in conftest.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

in test_foo.py you don't need to import api_url. Please notice that the api_url fixture from conftest.py is used in fixture data

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

import requests

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

test_bar(data):
    assert data is not None

回答by Alexandru Berbier

What I do in conftest.py:

我在 conftest.py 中所做的:


class StoreStuffHere:
    something_to_start_with = "value"
    somethingnew = None

#if you want an empty class:

class StoreStuffHere:
   pass

What I do in test_sample.py:

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

This will work for all the tests in the same module. If you're interested in using the same "storage" across test modules, use a dictionary instead or a named tupple instead of the class I used. In order to make sure you don't get missing values errors when certain tests fail, please initialize all known values with None.

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

回答by Joe Gasewicz

You can currently use the pytest object directly as stated in the Docs but only As a stopgap measure:

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

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

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

import pytest


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

But beware if using the namespaceversion as it's deprecated: https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace

但请注意,如果使用namespace已弃用的版本:https: //docs.pytest.org/en/latest/deprecations.html#pytest-namespace

old version using the namespace:

使用命名空间的旧版本:

class MySymbol:
    ...


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