378 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
			
		
		
	
	
			378 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
| .. _`skip and xfail`:
 | |
| 
 | |
| .. _skipping:
 | |
| 
 | |
| Skip and xfail: dealing with tests that cannot succeed
 | |
| ======================================================
 | |
| 
 | |
| You can mark test functions that cannot be run on certain platforms
 | |
| or that you expect to fail so pytest can deal with them accordingly and
 | |
| present a summary of the test session, while keeping the test suite *green*.
 | |
| 
 | |
| A **skip** means that you expect your test to pass only if some conditions are met,
 | |
| otherwise pytest should skip running the test altogether. Common examples are skipping
 | |
| windows-only tests on non-windows platforms, or skipping tests that depend on an external
 | |
| resource which is not available at the moment (for example a database).
 | |
| 
 | |
| A **xfail** means that you expect a test to fail for some reason.
 | |
| A common example is a test for a feature not yet implemented, or a bug not yet fixed.
 | |
| When a test passes despite being expected to fail (marked with ``pytest.mark.xfail``),
 | |
| it's an **xpass** and will be reported in the test summary.
 | |
| 
 | |
| ``pytest`` counts and lists *skip* and *xfail* tests separately. Detailed
 | |
| information about skipped/xfailed tests is not shown by default to avoid
 | |
| cluttering the output.  You can use the ``-r`` option to see details
 | |
| corresponding to the "short" letters shown in the test progress::
 | |
| 
 | |
|     pytest -rxXs  # show extra info on xfailed, xpassed, and skipped tests
 | |
| 
 | |
| More details on the ``-r`` option can be found by running ``pytest -h``.
 | |
| 
 | |
| (See :ref:`how to change command line options defaults`)
 | |
| 
 | |
| .. _skipif:
 | |
| .. _skip:
 | |
| .. _`condition booleans`:
 | |
| 
 | |
| Skipping test functions
 | |
| -----------------------
 | |
| 
 | |
| .. versionadded:: 2.9
 | |
| 
 | |
| The simplest way to skip a test function is to mark it with the ``skip`` decorator
 | |
| which may be passed an optional ``reason``:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     @pytest.mark.skip(reason="no way of currently testing this")
 | |
|     def test_the_unknown():
 | |
|         ...
 | |
| 
 | |
| 
 | |
| Alternatively, it is also possible to skip imperatively during test execution or setup
 | |
| by calling the ``pytest.skip(reason)`` function:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     def test_function():
 | |
|         if not valid_config():
 | |
|             pytest.skip("unsupported configuration")
 | |
| 
 | |
| It is also possible to skip the whole module using
 | |
| ``pytest.skip(reason, allow_module_level=True)`` at the module level:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     import pytest
 | |
| 
 | |
|     if not pytest.config.getoption("--custom-flag"):
 | |
|         pytest.skip("--custom-flag is missing, skipping tests", allow_module_level=True)
 | |
| 
 | |
| The imperative method is useful when it is not possible to evaluate the skip condition
 | |
| during import time.
 | |
| 
 | |
| **Reference**: :ref:`pytest.mark.skip ref`
 | |
| 
 | |
| ``skipif``
 | |
| ~~~~~~~~~~
 | |
| 
 | |
| .. versionadded:: 2.0
 | |
| 
 | |
| If you wish to skip something conditionally then you can use ``skipif`` instead.
 | |
| Here is an example of marking a test function to be skipped
 | |
| when run on an interpreter earlier than Python3.6 ::
 | |
| 
 | |
|     import sys
 | |
|     @pytest.mark.skipif(sys.version_info < (3,6),
 | |
|                         reason="requires python3.6 or higher")
 | |
|     def test_function():
 | |
|         ...
 | |
| 
 | |
| If the condition evaluates to ``True`` during collection, the test function will be skipped,
 | |
| with the specified reason appearing in the summary when using ``-rs``.
 | |
| 
 | |
| You can share ``skipif`` markers between modules.  Consider this test module::
 | |
| 
 | |
|     # content of test_mymodule.py
 | |
|     import mymodule
 | |
|     minversion = pytest.mark.skipif(mymodule.__versioninfo__ < (1,1),
 | |
|                                     reason="at least mymodule-1.1 required")
 | |
|     @minversion
 | |
|     def test_function():
 | |
|         ...
 | |
| 
 | |
| You can import the marker and reuse it in another test module::
 | |
| 
 | |
|     # test_myothermodule.py
 | |
|     from test_mymodule import minversion
 | |
| 
 | |
|     @minversion
 | |
|     def test_anotherfunction():
 | |
|         ...
 | |
| 
 | |
| For larger test suites it's usually a good idea to have one file
 | |
| where you define the markers which you then consistently apply
 | |
| throughout your test suite.
 | |
| 
 | |
| Alternatively, you can use :ref:`condition strings
 | |
| <string conditions>` instead of booleans, but they can't be shared between modules easily
 | |
| so they are supported mainly for backward compatibility reasons.
 | |
| 
 | |
| **Reference**: :ref:`pytest.mark.skipif ref`
 | |
| 
 | |
| 
 | |
| Skip all test functions of a class or module
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| You can use the ``skipif`` marker (as any other marker) on classes::
 | |
| 
 | |
|     @pytest.mark.skipif(sys.platform == 'win32',
 | |
|                         reason="does not run on windows")
 | |
|     class TestPosixCalls(object):
 | |
| 
 | |
|         def test_function(self):
 | |
|             "will not be setup or run under 'win32' platform"
 | |
| 
 | |
| If the condition is ``True``, this marker will produce a skip result for
 | |
| each of the test methods of that class.
 | |
| 
 | |
| If you want to skip all test functions of a module, you may use
 | |
| the ``pytestmark`` name on the global level:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     # test_module.py
 | |
|     pytestmark = pytest.mark.skipif(...)
 | |
| 
 | |
| If multiple ``skipif`` decorators are applied to a test function, it
 | |
| will be skipped if any of the skip conditions is true.
 | |
| 
 | |
| .. _`whole class- or module level`: mark.html#scoped-marking
 | |
| 
 | |
| 
 | |
| Skipping files or directories
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| Sometimes you may need to skip an entire file or directory, for example if the
 | |
| tests rely on Python version-specific features or contain code that you do not
 | |
| wish pytest to run. In this case, you must exclude the files and directories
 | |
| from collection. Refer to :ref:`customizing-test-collection` for more
 | |
| information.
 | |
| 
 | |
| 
 | |
| Skipping on a missing import dependency
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| You can use the following helper at module level
 | |
| or within a test or test setup function::
 | |
| 
 | |
|     docutils = pytest.importorskip("docutils")
 | |
| 
 | |
| If ``docutils`` cannot be imported here, this will lead to a
 | |
| skip outcome of the test.  You can also skip based on the
 | |
| version number of a library::
 | |
| 
 | |
|     docutils = pytest.importorskip("docutils", minversion="0.3")
 | |
| 
 | |
| The version will be read from the specified
 | |
| module's ``__version__`` attribute.
 | |
| 
 | |
| Summary
 | |
| ~~~~~~~
 | |
| 
 | |
| Here's a quick guide on how to skip tests in a module in different situations:
 | |
| 
 | |
| 1. Skip all tests in a module unconditionally:
 | |
| 
 | |
|   .. code-block:: python
 | |
| 
 | |
|         pytestmark = pytest.mark.skip("all tests still WIP")
 | |
| 
 | |
| 2. Skip all tests in a module based on some condition:
 | |
| 
 | |
|   .. code-block:: python
 | |
| 
 | |
|         pytestmark = pytest.mark.skipif(sys.platform == "win32", "tests for linux only")
 | |
| 
 | |
| 3. Skip all tests in a module if some import is missing:
 | |
| 
 | |
|   .. code-block:: python
 | |
| 
 | |
|         pexpect = pytest.importorskip("pexpect")
 | |
| 
 | |
| 
 | |
| .. _xfail:
 | |
| 
 | |
| XFail: mark test functions as expected to fail
 | |
| ----------------------------------------------
 | |
| 
 | |
| You can use the ``xfail`` marker to indicate that you
 | |
| expect a test to fail::
 | |
| 
 | |
|     @pytest.mark.xfail
 | |
|     def test_function():
 | |
|         ...
 | |
| 
 | |
| This test will be run but no traceback will be reported
 | |
| when it fails. Instead terminal reporting will list it in the
 | |
| "expected to fail" (``XFAIL``) or "unexpectedly passing" (``XPASS``) sections.
 | |
| 
 | |
| Alternatively, you can also mark a test as ``XFAIL`` from within a test or setup function
 | |
| imperatively:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     def test_function():
 | |
|         if not valid_config():
 | |
|             pytest.xfail("failing configuration (but should work)")
 | |
| 
 | |
| This will unconditionally make ``test_function`` ``XFAIL``. Note that no other code is executed
 | |
| after ``pytest.xfail`` call, differently from the marker. That's because it is implemented
 | |
| internally by raising a known exception.
 | |
| 
 | |
| **Reference**: :ref:`pytest.mark.xfail ref`
 | |
| 
 | |
| 
 | |
| .. _`xfail strict tutorial`:
 | |
| 
 | |
| ``strict`` parameter
 | |
| ~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| .. versionadded:: 2.9
 | |
| 
 | |
| Both ``XFAIL`` and ``XPASS`` don't fail the test suite, unless the ``strict`` keyword-only
 | |
| parameter is passed as ``True``:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     @pytest.mark.xfail(strict=True)
 | |
|     def test_function():
 | |
|         ...
 | |
| 
 | |
| 
 | |
| This will make ``XPASS`` ("unexpectedly passing") results from this test to fail the test suite.
 | |
| 
 | |
| You can change the default value of the ``strict`` parameter using the
 | |
| ``xfail_strict`` ini option:
 | |
| 
 | |
| .. code-block:: ini
 | |
| 
 | |
|     [pytest]
 | |
|     xfail_strict=true
 | |
| 
 | |
| 
 | |
| ``reason`` parameter
 | |
| ~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| As with skipif_ you can also mark your expectation of a failure
 | |
| on a particular platform::
 | |
| 
 | |
|     @pytest.mark.xfail(sys.version_info >= (3,6),
 | |
|                        reason="python3.6 api changes")
 | |
|     def test_function():
 | |
|         ...
 | |
| 
 | |
| 
 | |
| ``raises`` parameter
 | |
| ~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| If you want to be more specific as to why the test is failing, you can specify
 | |
| a single exception, or a list of exceptions, in the ``raises`` argument.
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     @pytest.mark.xfail(raises=RuntimeError)
 | |
|     def test_function():
 | |
|         ...
 | |
| 
 | |
| Then the test will be reported as a regular failure if it fails with an
 | |
| exception not mentioned in ``raises``.
 | |
| 
 | |
| ``run`` parameter
 | |
| ~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| If a test should be marked as xfail and reported as such but should not be
 | |
| even executed, use the ``run`` parameter as ``False``:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     @pytest.mark.xfail(run=False)
 | |
|     def test_function():
 | |
|         ...
 | |
| 
 | |
| This is specially useful for xfailing tests that are crashing the interpreter and should be
 | |
| investigated later.
 | |
| 
 | |
| 
 | |
| Ignoring xfail
 | |
| ~~~~~~~~~~~~~~
 | |
| 
 | |
| By specifying on the commandline::
 | |
| 
 | |
|     pytest --runxfail
 | |
| 
 | |
| you can force the running and reporting of an ``xfail`` marked test
 | |
| as if it weren't marked at all. This also causes ``pytest.xfail`` to produce no effect.
 | |
| 
 | |
| Examples
 | |
| ~~~~~~~~
 | |
| 
 | |
| Here is a simple test file with the several usages:
 | |
| 
 | |
| .. literalinclude:: example/xfail_demo.py
 | |
| 
 | |
| Running it with the report-on-xfail option gives this output::
 | |
| 
 | |
|     example $ pytest -rx xfail_demo.py
 | |
|     =========================== test session starts ============================
 | |
|     platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
 | |
|     rootdir: $REGENDOC_TMPDIR/example, inifile:
 | |
|     collected 7 items
 | |
| 
 | |
|     xfail_demo.py xxxxxxx                                                [100%]
 | |
|     ========================= short test summary info ==========================
 | |
|     XFAIL xfail_demo.py::test_hello
 | |
|     XFAIL xfail_demo.py::test_hello2
 | |
|       reason: [NOTRUN]
 | |
|     XFAIL xfail_demo.py::test_hello3
 | |
|       condition: hasattr(os, 'sep')
 | |
|     XFAIL xfail_demo.py::test_hello4
 | |
|       bug 110
 | |
|     XFAIL xfail_demo.py::test_hello5
 | |
|       condition: pytest.__version__[0] != "17"
 | |
|     XFAIL xfail_demo.py::test_hello6
 | |
|       reason: reason
 | |
|     XFAIL xfail_demo.py::test_hello7
 | |
| 
 | |
|     ======================== 7 xfailed in 0.12 seconds =========================
 | |
| 
 | |
| .. _`skip/xfail with parametrize`:
 | |
| 
 | |
| Skip/xfail with parametrize
 | |
| ---------------------------
 | |
| 
 | |
| It is possible to apply markers like skip and xfail to individual
 | |
| test instances when using parametrize:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     import pytest
 | |
| 
 | |
| 
 | |
|     @pytest.mark.parametrize(
 | |
|         ("n", "expected"),
 | |
|         [
 | |
|             (1, 2),
 | |
|             pytest.param(1, 0, marks=pytest.mark.xfail),
 | |
|             pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
 | |
|             (2, 3),
 | |
|             (3, 4),
 | |
|             (4, 5),
 | |
|             pytest.param(
 | |
|                 10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")
 | |
|             ),
 | |
|         ],
 | |
|     )
 | |
|     def test_increment(n, expected):
 | |
|         assert n + 1 == expected
 |