你可以在pytest中运行带有包含空格的字符串参数的单参数化测试吗?

huangapple go评论69阅读模式
英文:

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(&#39;param&#39;, [&#39;my param&#39;, &#39;my param 2&#39;])
    def test_param(self,param):
        ...

This works fine when calling this test with

python3 -m pytest -s -k &quot;test_param&quot;

However, if I want to target a specific test as following:

python3 -m pytest -s -k &quot;test_param[my param]&quot;

I get the error message

ERROR: Wrong expression passed to &#39;-k&#39;: my param: at column 4: expected end of input; got identifier

Also, when my input string contains a quotation mark &#39;, I get the error

ERROR: Wrong expression passed to &#39;-k&#39;: ... : at column 51: expected end of input; got left parenthesis

and if my string contains both &quot; and &#39;, 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(&#39;input, expected&#39;, 
            [
                (
&quot;&quot;&quot;
  integer :: &amp;
    my_var                                                                                            !&lt; my comment
&quot;&quot;&quot;,
{&#39;my_var&#39;: &#39;my comment&#39;}
                )
])
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(&#39;param&#39;, [&#39;my param&#39;, &#39;my param 2&#39;])
def test_param(param):
    print (os.environ[&quot;PYTEST_CURRENT_TEST&quot;])

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\[&quot;my param&quot;]
= 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选项可以在表达式中使用andnotor

@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(&#39;param&#39;, [&#39;my param&#39;, &#39;my param 2&#39;])
def test_param(param):
    print(param)

With python -m pytest -s -k &quot;test_param[my and param]&quot;

collected 2 items / 1 deselected / 1 selected

test_file.py
my param
.

With python -m pytest -s -k &quot;test_param[my or param]&quot;

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(&quot;param1,param2&quot;,
                             [
                                 (&quot;a1&quot;,&quot;a2&quot;),
                                 (&quot;p\&quot;1&quot;,&quot;p\&quot;2&quot;),
                                 (&quot;p&#39;1&quot;,&quot;p&#39;2&quot;),
                                 (&quot;p 1&quot;,&quot;p 2&quot;)
                             ]
                             )
    def test_smokeTest(self, param1, param2):
        print(&quot;param1 - &quot;, param1)
        print(&quot;param2 - &quot;, 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[&quot;a1-a2&quot;]

pytest -s test_2.py::Test_SmokeTests::test_smokeTest[&quot;p\&quot;1-p\&quot;2&quot;]

pytest -s test_2.py::Test_SmokeTests::test_smokeTest[&quot;p&#39;1-p&#39;2&quot;]

pytest -s test_2.py::Test_SmokeTests::test_smokeTest[&quot;p 1-p 2&quot;]

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

C:\Program Files\Anaconda3\envs\py38\Scripts&gt;pytest -s c://dev/Projects/deep-QA-Frontend_Pycharm/Deep_FE_TestAutomation_local/Tests/Playground/test_2.py::Test_SmokeTests::test_smokeTest[&quot;p 1-p 2&quot;]
========================================================================================================== 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&gt;pytest -s c://dev/Projects/deep-QA-Frontend_Pycharm/Deep_FE_TestAutomation_local/Tests/Playground/test_2.py::Test_SmokeTests::test_smokeTest[&quot;p&#39;1-p&#39;2&quot;]
========================================================================================================== 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&#39;1
param2 -  p&#39;2
.

=========================================================================================================== 1 passed in 0.04s ============================================================================================================

C:\Program Files\Anaconda3\envs\py38\Scripts&gt;pytest -s c://dev/Projects/deep-QA-Frontend_Pycharm/Deep_FE_TestAutomation_local/Tests/Playground/test_2.py::Test_SmokeTests::test_smokeTest[&quot;p\&quot;1-p\&quot;2&quot;]
========================================================================================================== 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&quot;1
param2 -  p&quot;2
.

<!-- end snippet -->

Check the results here

答案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[&quot;param_name&quot;]

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(
        &quot;param_1_name, param_2_name&quot;,
        [
            (&quot;Some difficult param 1&quot;, {&quot;a&quot;: 1}),
            (&quot;Some difficult param 2&quot;, {&quot;b&quot;: 2}),
        ],
        ids=[&quot;id1&quot;, &quot;id2&quot;]
    )
    def test_1111(self, request, param_1_name, param_2_name):

or

class TestClass111:
    @pytest.mark.parametrize(
        &quot;param_1_name, param_2_name&quot;,
        [
            pytest.param(&quot;Some difficult param 1&quot;, {&quot;a&quot;: 1}, id=&quot;id1&quot;),
            pytest.param(&quot;Some difficult param 2&quot;, {&quot;b&quot;: 2}, id=&quot;id2&quot;),
        ],
    )
    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[&quot;id1&quot;]

答案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 = [&quot;my param 1&quot;, &quot;my param 2&quot;]

def rpl_space(text: str):
    return text.replace(&quot; &quot;,&quot;_&quot;)

@pytest.mark.parametrize(&quot;param&quot;, 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                                                                                                                                  

&lt;Module tests/test_params_spaced.py&gt;
  &lt;Function test_spaced[my_param_1]&gt;
  &lt;Function test_spaced[my_param_2]&gt;

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>



huangapple
  • 本文由 发表于 2023年1月9日 19:11:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/75056435.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定