Marking test functions with attributes

By using the pytest.mark helper you can easily set metadata on your test functions. There are some builtin markers, for example:

  • skip - always skip a test function
  • skipif - skip a test function if a certain condition is met
  • xfail - produce an “expected failure” outcome if a certain condition is met
  • parametrize to perform multiple calls to the same test function.

It’s easy to create custom markers or to apply markers to whole test classes or modules. See Working with custom markers for examples which also serve as documentation.

Note

Marks can only be applied to tests, having no effect on fixtures.

class Mark[source]
name = None

name of the mark

args = None

positional arguments of the mark decorator

kwargs = None

keyword arguments of the mark decorator

combined_with(other)[source]
Parameters:other (Mark) – the mark to combine with
Return type:Mark

combines by appending aargs and merging the mappings

Marker revamp and iteration

New in version 3.6.

pytest’s marker implementation traditionally worked by simply updating the __dict__ attribute of functions to add markers, in a cumulative manner. As a result of the this, markers would unintendely be passed along class hierarchies in surprising ways plus the API for retriving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the @pytest.mark decorator and markers added via node.add_marker.

This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages.

Depending on how a marker got declared/changed one would get either a MarkerInfo which might contain markers from sibling classes, MarkDecorators when marks came from parameterization or from a node.add_marker call, discarding prior marks. Also MarkerInfo acts like a single mark, when it in fact repressents a merged view on multiple marks with the same name.

On top of that markers where not accessible the same way for modules, classes, and functions/methods, in fact, markers where only accessible in functions, even if they where declared on classes/modules.

A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with the initial design, providing _pytest.nodes.Node.iter_markers() method to iterate over markers in a consistent manner and reworking the internals, which solved great deal of problems with the initial design.

Updating code

The old Node.get_marker(name) function is considered deprecated because it returns an internal MarkerInfo object which contains the merged name, *args and **kwargs** of all the markers which apply to that node.

In general there are two scenarios on how markers should be handled:

1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g. log_level('info') at a module level can be overwritten by log_level('debug') for a specific test.

In this case replace use Node.get_closest_marker(name):

# replace this:
marker = item.get_marker('log_level')
if marker:
    level = marker.args[0]

# by this:
marker = item.get_closest_marker('log_level')
if marker:
    level = marker.args[0]

2. Marks compose additive. E.g. skipif(condition) marks means you just want to evaluate all of them, order doesn’t even matter. You probably want to think of your marks as a set here.

In this case iterate over each mark and handle their *args and **kwargs individually.

# replace this
skipif = item.get_marker('skipif')
if skipif:
    for condition in skipif.args:
        # eval condition

# by this:
for skipif in item.iter_markers('skipif'):
    condition = skipif.args[0]
    # eval condition

If you are unsure or have any questions, please consider opening an issue.