英文:
How can you run singular parametrized tests in pytest if the parameter is a string that contains spaces?
问题
这部分内容的中文翻译如下:
我有一个测试,看起来如下:
@pytest.mark.parametrize('param', ['我的参数', '我的参数2'])
def test_param(self, param):
...
当使用以下方式调用此测试时,工作正常:
python3 -m pytest -s -k "test_param"
但是,如果我想要针对特定的测试,如下所示:
python3 -m pytest -s -k "test_param[我的参数]"
我会收到错误消息:
错误:错误的表达式传递给 '-k':我的参数:在第4列处预期输入结束;得到标识符
此外,如果我的输入字符串包含引号 `''`,我会收到错误消息:
错误:错误的表达式传递给 '-k':... :在第51列处预期输入结束;得到左括号
如果我的字符串同时包含 `''` 和 `""`,我将完全无法使用 `-k` 选项调用它,因为字符串会在中间终止。
我应该如何运行包含这些符号的字符串参数的测试?目前,我正在创建一个字典,并提供 `range(len(my_dict))` 作为参数,以便可以通过索引访问这些变量,但我更希望能够直接在命令行中输入它们。
编辑:
目前的建议都很好,已经解决了我的一些问题。但是,如果我的测试函数看起来像这样(与这个最小示例不同,它有多个条目):
@pytest.mark.parametrize('input, expected',
[
(
"""
integer :: &
my_var !< 我的注释
""",
{'my_var': '我的注释'}
)
])
def test_fetch_variable_definitions_multiline(input, expected):
...
如果我的测试函数看起来像上面的示例一样,我还不确定如何调用单个测试。
英文:
I have a test that looks as following:
@pytest.mark.parametrize('param', ['my param', 'my param 2'])
def test_param(self,param):
...
This works fine when calling this test with
python3 -m pytest -s -k "test_param"
However, if I want to target a specific test as following:
python3 -m pytest -s -k "test_param[my param]"
I get the error message
ERROR: Wrong expression passed to '-k': my param: at column 4: expected end of input; got identifier
Also, when my input string contains a quotation mark '
, I get the error
ERROR: Wrong expression passed to '-k': ... : at column 51: expected end of input; got left parenthesis
and if my string contains both "
and '
, I am completely unable to call it with the -k
option without the string terminating in the middle.
How can I run tests with string parameters that contain these symbols? I am currently creating a dict and supplying range(len(my_dict))
as the parameter so I can access these variables via index, but I would prefer to be able to directly enter them in the commandline.
EDIT:
The current suggestions are all great and already solve some of my problems. However, I'm still not sure how I would call singular tests if my test function looked like this (it has more than one entry as opposed to this minimal example):
@pytest.mark.parametrize('input, expected',
[
(
"""
integer :: &
my_var !< my comment
""",
{'my_var': 'my comment'}
)
])
def test_fetch_variable_definitions_multiline(input,expected):
...
答案1
得分: 2
Wow. This was an interesting rabbit hole to go down.
You need to access the test you want to run via its nodeid. Node ids are assigned by class::method::param
.
But your param has spaces, and that makes it hard to pass in via the shell. I ended up using this to figure out what the node ids actually are:
import pytest
import os
@pytest.mark.parametrize('param', ['my param', 'my param 2'])
def test_param(param):
print(os.environ["PYTEST_CURRENT_TEST"])
That gave me this for output:
so.py:7: RuntimeError
- Captured stdout call -
so.py::test_param[my param] (call)
Which is not terribly dissimilar to what you tried to pass in via the shell, and that already failed. But that is the nodeid. A small bit of testing later, and this seems to work:
~ % python3.9 -m pytest so.py::test_param["my param"]
= test session starts =
platform darwin -- Python 3.9.2, pytest-7.2.0, pluggy-1.0.0
collected 1 item
so.py . [100%]
= 1 passed in 0.01s =
英文:
Wow. This was an interesting rabbit hole to go down.
You need to access the test you want to run via its nodeid. Node ids are assigned by class::method::param
.
But your param has spaces, and that makes it hard to pass in via the shell. I ended up using this to figure out what the node ids actually are:
import pytest
import os
@pytest.mark.parametrize('param', ['my param', 'my param 2'])
def test_param(param):
print (os.environ["PYTEST_CURRENT_TEST"])
That gave me this for output:
so.py:7: RuntimeError
- Captured stdout call -
so.py::test_param[my param] (call)
Which is not terribly dissimilar to what you tried to pass in via the shell, and that already failed. But that is the nodeid. A small bit of testing later, and this seems to work:
~ % python3.9 -m pytest so.py::test_param\["my param"]
= test session starts =
platform darwin -- Python 3.9.2, pytest-7.2.0, pluggy-1.0.0
collected 1 item
so.py . [100%]
= 1 passed in 0.01s =
答案2
得分: 1
使用-k选项可以在表达式中使用and
、not
和or
。
@pytest.mark.parametrize('param', ['my param', 'my param 2'])
def test_param(param):
print(param)
使用python -m pytest -s -k "test_param[my and param]"
:
collected 2 items / 1 deselected / 1 selected
test_file.py
my param
.
使用python -m pytest -s -k "test_param[my or param]"
:
collected 2 items
test_file.py
my param
.
my param 2
.
英文:
The -k option can use and
, not
and or
in the expression
@pytest.mark.parametrize('param', ['my param', 'my param 2'])
def test_param(param):
print(param)
With python -m pytest -s -k "test_param[my and param]"
collected 2 items / 1 deselected / 1 selected
test_file.py
my param
.
With python -m pytest -s -k "test_param[my or param]"
collected 2 items
test_file.py
my param
.
my param 2
.
答案3
得分: 1
你可以在你的命令中使用 class::method[param1-param2-param3]
结构。
# Python文件test_2.py包含以下内容
import pytest
import os
class Test_SmokeTests:
@pytest.mark.parametrize("param1,param2",
[
("a1","a2"),
("p\"1","p\"2"),
("p'1","p'2"),
("p 1","p 2")
]
)
def test_smokeTest(self, param1, param2):
print("param1 - ", param1)
print("param2 - ", param2)
这对于四组参数都有效,即没有空格,带有 '
,带有 "
和带有空格的情况。它还适用于多个参数。在这种情况下,参数需要用短横线(-)分隔。
pytest -s test_2.py::Test_SmokeTests::test_smokeTest["a1-a2"]
pytest -s test_2.py::Test_SmokeTests::test_smokeTest["p\"1-p\"2"]
pytest -s test_2.py::Test_SmokeTests::test_smokeTest["p'1-p'2"]
pytest -s test_2.py::Test_SmokeTests::test_smokeTest["p 1-p 2"]
英文:
You can use the class::method[param1-param2-param3]
construct in your command
#The python file test_2.py contains
import pytest
import os
class Test_SmokeTests:
@pytest.mark.parametrize("param1,param2",
[
("a1","a2"),
("p\"1","p\"2"),
("p'1","p'2"),
("p 1","p 2")
]
)
def test_smokeTest(self, param1, param2):
print("param1 - ", param1)
print("param2 - ", param2)
It works for each of the 4 sets of parameters i.e. with no spaces, with ', with " and with spaces.
It also works for multiple paramters. In this case, the parameters need to be separated by a dash (-)
pytest -s test_2.py::Test_SmokeTests::test_smokeTest["a1-a2"]
pytest -s test_2.py::Test_SmokeTests::test_smokeTest["p\"1-p\"2"]
pytest -s test_2.py::Test_SmokeTests::test_smokeTest["p'1-p'2"]
pytest -s test_2.py::Test_SmokeTests::test_smokeTest["p 1-p 2"]
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-html -->
C:\Program Files\Anaconda3\envs\py38\Scripts>pytest -s c://dev/Projects/deep-QA-Frontend_Pycharm/Deep_FE_TestAutomation_local/Tests/Playground/test_2.py::Test_SmokeTests::test_smokeTest["p 1-p 2"]
========================================================================================================== test session starts ===========================================================================================================
platform win32 -- Python 3.8.13, pytest-7.1.2, pluggy-1.0.0
rootdir: c:\
plugins: html-3.1.1, metadata-2.0.2, xdist-3.0.2
collected 1 item
..\..\..\..\..\dev\Projects\deep-QA-Frontend_Pycharm\Deep_FE_TestAutomation_local\Tests\Playground\test_2.py param1 - p 1
param2 - p 2
.
=========================================================================================================== 1 passed in 0.06s ============================================================================================================
C:\Program Files\Anaconda3\envs\py38\Scripts>pytest -s c://dev/Projects/deep-QA-Frontend_Pycharm/Deep_FE_TestAutomation_local/Tests/Playground/test_2.py::Test_SmokeTests::test_smokeTest["p'1-p'2"]
========================================================================================================== test session starts ===========================================================================================================
platform win32 -- Python 3.8.13, pytest-7.1.2, pluggy-1.0.0
rootdir: c:\
plugins: html-3.1.1, metadata-2.0.2, xdist-3.0.2
collected 1 item
..\..\..\..\..\dev\Projects\deep-QA-Frontend_Pycharm\Deep_FE_TestAutomation_local\Tests\Playground\test_2.py param1 - p'1
param2 - p'2
.
=========================================================================================================== 1 passed in 0.04s ============================================================================================================
C:\Program Files\Anaconda3\envs\py38\Scripts>pytest -s c://dev/Projects/deep-QA-Frontend_Pycharm/Deep_FE_TestAutomation_local/Tests/Playground/test_2.py::Test_SmokeTests::test_smokeTest["p\"1-p\"2"]
========================================================================================================== test session starts ===========================================================================================================
platform win32 -- Python 3.8.13, pytest-7.1.2, pluggy-1.0.0
rootdir: c:\
plugins: html-3.1.1, metadata-2.0.2, xdist-3.0.2
collected 1 item
..\..\..\..\..\dev\Projects\deep-QA-Frontend_Pycharm\Deep_FE_TestAutomation_local\Tests\Playground\test_2.py param1 - p"1
param2 - p"2
.
<!-- end snippet -->
答案4
得分: 1
以下是您问题的答案,包括“EDIT”部分的翻译:
您可以使用以下语法来运行 pytest:
pytest .\tests\test_package_1\test_module_1_1.py::TestClass111::test_1111["param_name"]
其中 param_name
可以是单个参数的值,也可以是 pytest param id
。
要为参数集分配一些 ID,您可以使用以下语法:
class TestClass111:
@pytest.mark.parametrize(
"param_1_name, param_2_name",
[
("Some difficult param 1", {"a": 1}),
("Some difficult param 2", {"b": 2}),
],
ids=["id1", "id2"]
)
def test_1111(self, request, param_1_name, param_2_name):
或者
class TestClass111:
@pytest.mark.parametrize(
"param_1_name, param_2_name",
[
pytest.param("Some difficult param 1", {"a": 1}, id="id1"),
pytest.param("Some difficult param 2", {"b": 2}, id="id2"),
],
)
def test_1111(self, request, param_1_name, param_2_name):
这样,您可以通过带有 id=id1
的参数集来访问测试,如下所示:
pytest .\tests\test_package_1\test_module_1_1.py::TestClass111::test_1111["id1"]
英文:
Answer to your question including "EDIT" section:
You can use following syntax to run pytest
pytest .\tests\test_package_1\test_module_1_1.py::TestClass111::test_1111["param_name"]
where param_name
can be both value of single parameter or pytest param id
To assign some id to parameter set you can use following syntax:
class TestClass111:
@pytest.mark.parametrize(
"param_1_name, param_2_name",
[
("Some difficult param 1", {"a": 1}),
("Some difficult param 2", {"b": 2}),
],
ids=["id1", "id2"]
)
def test_1111(self, request, param_1_name, param_2_name):
or
class TestClass111:
@pytest.mark.parametrize(
"param_1_name, param_2_name",
[
pytest.param("Some difficult param 1", {"a": 1}, id="id1"),
pytest.param("Some difficult param 2", {"b": 2}, id="id2"),
],
)
def test_1111(self, request, param_1_name, param_2_name):
so you can access test with param set with id=id1
like this:
pytest .\tests\test_package_1\test_module_1_1.py::TestClass111::test_1111["id1"]
答案5
得分: 1
I don't think it is possible to use -k
with a string that contains spaces.
How about adding pytest's built-in ids
parameter (which allows you to control the naming of your parametrized tests) and configuring it like this:
import pytest
import logging as log
test_data = ["my param 1", "my param 2"]
def replace_space(text: str):
return text.replace(" ", "_")
@pytest.mark.parametrize("param", test_data, ids=replace_space)
def test_spaced(param):
log.info(param)
pytest will then create tests that don't have spaces in their names.
Here is an output of how pytest 'sees' it using the --co
flag (which only collects tests):
$ pytest tests/test_params_spaced.py --co
collected 2 items
<Module tests/test_params_spaced.py>
<Function test_spaced[my_param_1]>
<Function test_spaced[my_param_2]>
And then I am able to run a single test like this:
$ pytest -k test_spaced[my_param_1]
tests/test_params_spaced.py::test_spaced[my_param_1]
------------------------------------------------------------------ live log call -------------------------------------------------------------------
INFO root:test_params_spaced.py:12 my param 1
PASSED
英文:
I don't think it is possible to use -k
with string that contains spaces.
How about adding pytest built-in ids
parameter (which allows you to control the naming of your parametrized tests) and configuring it like this:
import pytest
import logging as log
test_data = ["my param 1", "my param 2"]
def rpl_space(text: str):
return text.replace(" ","_")
@pytest.mark.parametrize("param", test_data, ids=rpl_space)
def test_spaced(param):
log.info(param)
pytest will then create tests that don't have spaces in names.
Here is an output of how pytest 'sees' it using --co
flag (which only collects tests):
$ pytest tests/test_params_spaced.py --co
collected 2 items
<Module tests/test_params_spaced.py>
<Function test_spaced[my_param_1]>
<Function test_spaced[my_param_2]>
and then I am able to run a single test like this:
$ pytest -k test_spaced[my_param_1]
tests/test_params_spaced.py::test_spaced[my_param_1]
------------------------------------------------------------------ live log call -------------------------------------------------------------------
INFO root:test_params_spaced.py:12 my param 1
PASSED
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论