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
How to share global variables between tests?
提问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
回答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_version
CLI option but accessing --mobile_api_ver
option 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_version
CLI 选项,但--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 namespace
version 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()}