pytestpytest-bddpytest-markers

Unknown marker with pytest-bdd only when parameter is declared


When I declare a marker in pytest.ini having a parameter, this is not recognized in pytest-bdd feature file. Markers without parameters seem to work fine.

[pytest]
markers = 
    swr(issue1): link to Software Requirement
    smoke: Smoke Test component

Simple feature file works fine with @smoke:

Feature: Trivial Example
    @smoke
    Scenario: Add a number to another number
        Given 7 is set
        When 9 is added
        Then new value is 16

Fails with @swr("123"):

Feature: Trivial Example
    @swr("123")
    Scenario: Add a number to another number
        Given 7 is set
        When 9 is added
        Then new value is 16

Failure is a warning:

../../../../../.local/lib/python3.10/site-packages/pytest_bdd/plugin.py:127
  /home/parallels/.local/lib/python3.10/site-packages/pytest_bdd/plugin.py:127: PytestUnknownMarkWarning: Unknown pytest.mark.swr("123") - is this a typo?  You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/how-to/mark.html
    mark = getattr(pytest.mark, tag)

Solution

  • Taking a look in the known issues in the repository I stumbled upon something that is related. The developer mentions there is a hook available which is seen here.

    In the conftest.py we can then do the following:

    from typing import Callable, cast
    import pytest
    import ast
    
    def pytest_bdd_apply_tag(tag: str, function) -> Callable:
        tree = ast.parse(tag)
        body = tree.body[0].value
        if isinstance(body, ast.Call):
            name = body.func.id
            arg = body.args[0].value
            mark = getattr(pytest.mark, name).with_args(arg)
        else:
            mark = getattr(pytest.mark, tag)
        marked = mark(function)
        return cast(Callable, marked)
    

    Then we can just register the marker as swr and the hook should automatically parametrize the function as needed. It uses ast to parse the marker and dynamically create the new marker. Shown below is what mark looks like when running with swr or swr("123").

    platform darwin -- Python 3.9.6, pytest-7.2.0, pluggy-1.0.0
    rootdir: ***, configfile: pytest.ini
    plugins: bdd-6.1.1
    collecting ... 
    MarkDecorator(mark=Mark(name='swr', args=('123',), kwargs={}))
    collected 1 item
    

    platform darwin -- Python 3.9.6, pytest-7.2.0, pluggy-1.0.0
    rootdir: ***, configfile: pytest.ini
    plugins: bdd-6.1.1
    collecting ... 
    MarkDecorator(mark=Mark(name='swr', args=(), kwargs={}))
    collected 1 item           
    
                                             
    

    Take note of MarkDecorator in the output for each of the calls.