From 96f0b47169d426c8d0e1e6200e55b5635d9c2796 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 29 Jul 2026 21:38:44 -0700 Subject: [PATCH] docs: document multiple positional conditions for skipif/xfail pytest.mark.skipif and pytest.mark.xfail both accept multiple positional condition arguments (the test is skipped/xfailed if any evaluate to True), as already stated in the internal marker registration message and encoded in the type stubs. The reference docs only showed a single `condition` parameter and never mentioned this. Also add regression tests, since no test previously exercised multiple conditions passed to a single mark call (only stacking separate mark decorators was covered). --- doc/en/reference/reference.rst | 11 ++++++++-- testing/test_skipping.py | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 4f2e0b70850..48a440e9a16 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -228,10 +228,14 @@ pytest.mark.skipif Skip a test function if a condition is ``True``. -.. py:function:: pytest.mark.skipif(condition, *, reason=None) +.. py:function:: pytest.mark.skipif(condition, *conditions, reason=None) :type condition: bool or str :param condition: ``True/False`` if the condition should be skipped or a :ref:`condition string `. + :type conditions: bool or str + :param conditions: + Additional conditions, evaluated the same way as ``condition``. The test is + skipped if *any* of the conditions evaluate to ``True``. :keyword str reason: Reason why the test function is being skipped. @@ -266,12 +270,15 @@ pytest.mark.xfail Marks a test function as *expected to fail*. -.. py:function:: pytest.mark.xfail(condition=True, *, reason=None, raises=None, run=True, strict=strict_xfail) +.. py:function:: pytest.mark.xfail(condition=True, *conditions, reason=None, raises=None, run=True, strict=strict_xfail) :keyword Union[bool, str] condition: Condition for marking the test function as xfail (``True/False`` or a :ref:`condition string `). If a ``bool``, you also have to specify ``reason`` (see :ref:`condition string `). + :keyword Union[bool, str] conditions: + Additional conditions, evaluated the same way as ``condition``. The test is + marked as xfail if *any* of the conditions evaluate to ``True``. :keyword str reason: Reason why the test function is marked as xfail. :keyword raises: diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 5bb641aed3c..1c4b0ffcfa6 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -70,6 +70,43 @@ def test_func(): assert skipped assert skipped.reason == "hello world" + def test_marked_multiple_args_skipif(self, pytester: Pytester) -> None: + item = pytester.getitem( + """ + import pytest + @pytest.mark.skipif(False, True, reason="hello world") + def test_func(): + pass + """ + ) + skipped = evaluate_skip_marks(item) + assert skipped + assert skipped.reason == "hello world" + + def test_marked_multiple_args_skipif_all_false(self, pytester: Pytester) -> None: + item = pytester.getitem( + """ + import pytest + @pytest.mark.skipif(False, False, reason="hello world") + def test_func(): + pass + """ + ) + assert evaluate_skip_marks(item) is None + + def test_marked_multiple_args_xfail(self, pytester: Pytester) -> None: + item = pytester.getitem( + """ + import pytest + @pytest.mark.xfail(False, True, reason="hello world") + def test_func(): + pass + """ + ) + xfailed = evaluate_xfail_marks(item) + assert xfailed + assert xfailed.reason == "hello world" + def test_marked_one_arg_twice(self, pytester: Pytester) -> None: lines = [ """@pytest.mark.skipif("not hasattr(os, 'murks')")""",