Compare commits
40 Commits
Author | SHA1 | Date |
---|---|---|
|
e886b170dd | |
|
45a645db86 | |
|
f5b1f44317 | |
|
89d03b2999 | |
|
5e14e7d9c1 | |
|
5819cb611e | |
|
687146e3f1 | |
|
ae61fa4040 | |
|
d7d089469e | |
|
6933bf6b4d | |
|
819fef87a7 | |
|
604047ee1a | |
|
52363fe560 | |
|
c923dbc96f | |
|
2d680da78b | |
|
b3c444ddc1 | |
|
fe69bd5baf | |
|
09b1d7cc99 | |
|
ea65ea877e | |
|
f4f30d7073 | |
|
309810ac2c | |
|
e63fac3aec | |
|
cb91c5033e | |
|
9a879ee23e | |
|
e9d18bd8ac | |
|
912870d33e | |
|
0115b716c0 | |
|
9a91b67eeb | |
|
79d0d3eff4 | |
|
834f55eddb | |
|
6110f84f78 | |
|
5a339f0d74 | |
|
022bff27a7 | |
|
92af2e22d2 | |
|
0307213254 | |
|
df7b26704d | |
|
1516780829 | |
|
b945b39b0b | |
|
2d5b8a85c2 | |
|
8963644da3 |
|
@ -41,6 +41,7 @@ jobs:
|
||||||
|
|
||||||
"docs",
|
"docs",
|
||||||
"doctesting",
|
"doctesting",
|
||||||
|
"plugins",
|
||||||
]
|
]
|
||||||
|
|
||||||
include:
|
include:
|
||||||
|
@ -111,6 +112,11 @@ jobs:
|
||||||
tox_env: "py38-xdist"
|
tox_env: "py38-xdist"
|
||||||
use_coverage: true
|
use_coverage: true
|
||||||
|
|
||||||
|
- name: "plugins"
|
||||||
|
python: "3.7"
|
||||||
|
os: ubuntu-latest
|
||||||
|
tox_env: "plugins"
|
||||||
|
|
||||||
- name: "docs"
|
- name: "docs"
|
||||||
python: "3.7"
|
python: "3.7"
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fixed an internal error crash with ``IndexError: list index out of range`` when
|
||||||
|
collecting a module which starts with a decorated function, the decorator
|
||||||
|
raises, and assertion rewriting is enabled.
|
|
@ -1,2 +0,0 @@
|
||||||
Fix pylint ``not-callable`` lint on ``pytest.mark.parametrize()`` and the other builtin marks:
|
|
||||||
``skip``, ``skipif``, ``xfail``, ``usefixtures``, ``filterwarnings``.
|
|
|
@ -1 +0,0 @@
|
||||||
Fix regression in plugins using ``TestReport.longreprtext`` (such as ``pytest-html``) when ``TestReport.longrepr`` is not a string.
|
|
|
@ -0,0 +1 @@
|
||||||
|
When a plugin listed in ``required_plugins`` is missing, a simple error message is now shown instead of a stacktrace.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix INTERNALERROR when accessing locals / globals with faulty ``exec``.
|
|
@ -6,6 +6,8 @@ Release announcements
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
|
||||||
|
release-6.0.2
|
||||||
|
release-6.0.1
|
||||||
release-6.0.0
|
release-6.0.0
|
||||||
release-6.0.0rc1
|
release-6.0.0rc1
|
||||||
release-5.4.3
|
release-5.4.3
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
pytest-6.0.1
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 6.0.1 has just been released to PyPI.
|
||||||
|
|
||||||
|
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||||
|
|
||||||
|
pip install --upgrade pytest
|
||||||
|
|
||||||
|
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||||
|
|
||||||
|
Thanks to all who contributed to this release, among them:
|
||||||
|
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Mattreex
|
||||||
|
* Ran Benita
|
||||||
|
* hp310780
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -0,0 +1,19 @@
|
||||||
|
pytest-6.0.2
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 6.0.2 has just been released to PyPI.
|
||||||
|
|
||||||
|
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||||
|
|
||||||
|
pip install --upgrade pytest
|
||||||
|
|
||||||
|
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||||
|
|
||||||
|
Thanks to all of the contributors to this release:
|
||||||
|
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Ran Benita
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -28,6 +28,45 @@ with advance notice in the **Deprecations** section of releases.
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
pytest 6.0.2 (2020-09-04)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#7148 <https://github.com/pytest-dev/pytest/issues/7148>`_: Fixed ``--log-cli`` potentially causing unrelated ``print`` output to be swallowed.
|
||||||
|
|
||||||
|
|
||||||
|
- `#7672 <https://github.com/pytest-dev/pytest/issues/7672>`_: Fixed log-capturing level restored incorrectly if ``caplog.set_level`` is called more than once.
|
||||||
|
|
||||||
|
|
||||||
|
- `#7686 <https://github.com/pytest-dev/pytest/issues/7686>`_: Fixed `NotSetType.token` being used as the parameter ID when the parametrization list is empty.
|
||||||
|
Regressed in pytest 6.0.0.
|
||||||
|
|
||||||
|
|
||||||
|
- `#7707 <https://github.com/pytest-dev/pytest/issues/7707>`_: Fix internal error when handling some exceptions that contain multiple lines or the style uses multiple lines (``--tb=line`` for example).
|
||||||
|
|
||||||
|
|
||||||
|
pytest 6.0.1 (2020-07-30)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#7394 <https://github.com/pytest-dev/pytest/issues/7394>`_: Passing an empty ``help`` value to ``Parser.add_option`` is now accepted instead of crashing when running ``pytest --help``.
|
||||||
|
Passing ``None`` raises a more informative ``TypeError``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#7558 <https://github.com/pytest-dev/pytest/issues/7558>`_: Fix pylint ``not-callable`` lint on ``pytest.mark.parametrize()`` and the other builtin marks:
|
||||||
|
``skip``, ``skipif``, ``xfail``, ``usefixtures``, ``filterwarnings``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#7559 <https://github.com/pytest-dev/pytest/issues/7559>`_: Fix regression in plugins using ``TestReport.longreprtext`` (such as ``pytest-html``) when ``TestReport.longrepr`` is not a string.
|
||||||
|
|
||||||
|
|
||||||
|
- `#7569 <https://github.com/pytest-dev/pytest/issues/7569>`_: Fix logging capture handler's level not reset on teardown after a call to ``caplog.set_level()``.
|
||||||
|
|
||||||
|
|
||||||
pytest 6.0.0 (2020-07-28)
|
pytest 6.0.0 (2020-07-28)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
@ -194,7 +233,7 @@ Breaking Changes
|
||||||
- `#7224 <https://github.com/pytest-dev/pytest/issues/7224>`_: The `item.catch_log_handler` and `item.catch_log_handlers` attributes, set by the
|
- `#7224 <https://github.com/pytest-dev/pytest/issues/7224>`_: The `item.catch_log_handler` and `item.catch_log_handlers` attributes, set by the
|
||||||
logging plugin and never meant to be public, are no longer available.
|
logging plugin and never meant to be public, are no longer available.
|
||||||
|
|
||||||
The deprecated ``--no-print-logs`` option is removed. Use ``--show-capture`` instead.
|
The deprecated ``--no-print-logs`` option and ``log_print`` ini option are removed. Use ``--show-capture`` instead.
|
||||||
|
|
||||||
|
|
||||||
- `#7226 <https://github.com/pytest-dev/pytest/issues/7226>`_: Removed the unused ``args`` parameter from ``pytest.Function.__init__``.
|
- `#7226 <https://github.com/pytest-dev/pytest/issues/7226>`_: Removed the unused ``args`` parameter from ``pytest.Function.__init__``.
|
||||||
|
|
|
@ -51,9 +51,10 @@ a public API and may break in the future.
|
||||||
.. versionremoved:: 6.0
|
.. versionremoved:: 6.0
|
||||||
|
|
||||||
|
|
||||||
Option ``--no-print-logs`` is removed. If you used ``--no-print-logs``, please use ``--show-capture`` instead.
|
The ``--no-print-logs`` option and ``log_print`` ini setting are removed. If
|
||||||
|
you used them, please use ``--show-capture`` instead.
|
||||||
|
|
||||||
``--show-capture`` command-line option was added in ``pytest 3.5.0`` and allows to specify how to
|
A ``--show-capture`` command-line option was added in ``pytest 3.5.0`` which allows to specify how to
|
||||||
display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log`` or ``all`` (the default).
|
display captured output when tests fail: ``no``, ``stdout``, ``stderr``, ``log`` or ``all`` (the default).
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1136,8 +1136,8 @@ and teared down after every test that used it.
|
||||||
|
|
||||||
.. _`usefixtures`:
|
.. _`usefixtures`:
|
||||||
|
|
||||||
Using fixtures from classes, modules or projects
|
Use fixtures in classes and modules with ``usefixtures``
|
||||||
----------------------------------------------------------------------
|
--------------------------------------------------------
|
||||||
|
|
||||||
.. regendoc:wipe
|
.. regendoc:wipe
|
||||||
|
|
||||||
|
@ -1531,3 +1531,37 @@ Given the tests file structure is:
|
||||||
In the example above, a parametrized fixture is overridden with a non-parametrized version, and
|
In the example above, a parametrized fixture is overridden with a non-parametrized version, and
|
||||||
a non-parametrized fixture is overridden with a parametrized version for certain test module.
|
a non-parametrized fixture is overridden with a parametrized version for certain test module.
|
||||||
The same applies for the test folder level obviously.
|
The same applies for the test folder level obviously.
|
||||||
|
|
||||||
|
|
||||||
|
Using fixtures from other projects
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
Usually projects that provide pytest support will use :ref:`entry points <setuptools entry points>`,
|
||||||
|
so just installing those projects into an environment will make those fixtures available for use.
|
||||||
|
|
||||||
|
In case you want to use fixtures from a project that does not use entry points, you can
|
||||||
|
define :globalvar:`pytest_plugins` in your top ``conftest.py`` file to register that module
|
||||||
|
as a plugin.
|
||||||
|
|
||||||
|
Suppose you have some fixtures in ``mylibrary.fixtures`` and you want to reuse them into your
|
||||||
|
``app/tests`` directory.
|
||||||
|
|
||||||
|
All you need to do is to define :globalvar:`pytest_plugins` in ``app/tests/conftest.py``
|
||||||
|
pointing to that module.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
pytest_plugins = "mylibrary.fixtures"
|
||||||
|
|
||||||
|
This effectively registers ``mylibrary.fixtures`` as a plugin, making all its fixtures and
|
||||||
|
hooks available to tests in ``app/tests``.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Sometimes users will *import* fixtures from other projects for use, however this is not
|
||||||
|
recommended: importing fixtures into a module will register them in pytest
|
||||||
|
as *defined* in that module.
|
||||||
|
|
||||||
|
This has minor consequences, such as appearing multiple times in ``pytest --help``,
|
||||||
|
but it is not **recommended** because this behavior might change/stop working
|
||||||
|
in future versions.
|
||||||
|
|
|
@ -28,7 +28,7 @@ Install ``pytest``
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ pytest --version
|
$ pytest --version
|
||||||
pytest 6.0.0
|
pytest 6.0.2
|
||||||
|
|
||||||
.. _`simpletest`:
|
.. _`simpletest`:
|
||||||
|
|
||||||
|
|
|
@ -783,12 +783,19 @@ ExceptionInfo
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
pytest.ExitCode
|
ExitCode
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
.. autoclass:: _pytest.config.ExitCode
|
.. autoclass:: _pytest.config.ExitCode
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
File
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
.. autoclass:: _pytest.nodes.File()
|
||||||
|
:members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
|
||||||
FixtureDef
|
FixtureDef
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
@ -1466,20 +1473,6 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||||
For more information, see :ref:`logging`.
|
For more information, see :ref:`logging`.
|
||||||
|
|
||||||
|
|
||||||
.. confval:: log_print
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If set to ``False``, will disable displaying captured logging messages for failed tests.
|
|
||||||
|
|
||||||
.. code-block:: ini
|
|
||||||
|
|
||||||
[pytest]
|
|
||||||
log_print = False
|
|
||||||
|
|
||||||
For more information, see :ref:`logging`.
|
|
||||||
|
|
||||||
|
|
||||||
.. confval:: markers
|
.. confval:: markers
|
||||||
|
|
||||||
When the ``--strict-markers`` or ``--strict`` command-line arguments are used,
|
When the ``--strict-markers`` or ``--strict`` command-line arguments are used,
|
||||||
|
@ -1666,3 +1659,296 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
xfail_strict = True
|
xfail_strict = True
|
||||||
|
|
||||||
|
|
||||||
|
.. _`command-line-flags`:
|
||||||
|
|
||||||
|
Command-line Flags
|
||||||
|
------------------
|
||||||
|
|
||||||
|
All the command-line flags can be obtained by running ``pytest --help``::
|
||||||
|
|
||||||
|
$ pytest --help
|
||||||
|
usage: pytest [options] [file_or_dir] [file_or_dir] [...]
|
||||||
|
|
||||||
|
positional arguments:
|
||||||
|
file_or_dir
|
||||||
|
|
||||||
|
general:
|
||||||
|
-k EXPRESSION only run tests which match the given substring
|
||||||
|
expression. An expression is a python evaluatable
|
||||||
|
expression where all names are substring-matched
|
||||||
|
against test names and their parent classes.
|
||||||
|
Example: -k 'test_method or test_other' matches all
|
||||||
|
test functions and classes whose name contains
|
||||||
|
'test_method' or 'test_other', while -k 'not
|
||||||
|
test_method' matches those that don't contain
|
||||||
|
'test_method' in their names. -k 'not test_method
|
||||||
|
and not test_other' will eliminate the matches.
|
||||||
|
Additionally keywords are matched to classes and
|
||||||
|
functions containing extra names in their
|
||||||
|
'extra_keyword_matches' set, as well as functions
|
||||||
|
which have names assigned directly to them. The
|
||||||
|
matching is case-insensitive.
|
||||||
|
-m MARKEXPR only run tests matching given mark expression.
|
||||||
|
For example: -m 'mark1 and not mark2'.
|
||||||
|
--markers show markers (builtin, plugin and per-project ones).
|
||||||
|
-x, --exitfirst exit instantly on first error or failed test.
|
||||||
|
--fixtures, --funcargs
|
||||||
|
show available fixtures, sorted by plugin appearance
|
||||||
|
(fixtures with leading '_' are only shown with '-v')
|
||||||
|
--fixtures-per-test show fixtures per test
|
||||||
|
--pdb start the interactive Python debugger on errors or
|
||||||
|
KeyboardInterrupt.
|
||||||
|
--pdbcls=modulename:classname
|
||||||
|
start a custom interactive Python debugger on
|
||||||
|
errors. For example:
|
||||||
|
--pdbcls=IPython.terminal.debugger:TerminalPdb
|
||||||
|
--trace Immediately break when running each test.
|
||||||
|
--capture=method per-test capturing method: one of fd|sys|no|tee-sys.
|
||||||
|
-s shortcut for --capture=no.
|
||||||
|
--runxfail report the results of xfail tests as if they were
|
||||||
|
not marked
|
||||||
|
--lf, --last-failed rerun only the tests that failed at the last run (or
|
||||||
|
all if none failed)
|
||||||
|
--ff, --failed-first run all tests, but run the last failures first.
|
||||||
|
This may re-order tests and thus lead to repeated
|
||||||
|
fixture setup/teardown.
|
||||||
|
--nf, --new-first run tests from new files first, then the rest of the
|
||||||
|
tests sorted by file mtime
|
||||||
|
--cache-show=[CACHESHOW]
|
||||||
|
show cache contents, don't perform collection or
|
||||||
|
tests. Optional argument: glob (default: '*').
|
||||||
|
--cache-clear remove all cache contents at start of test run.
|
||||||
|
--lfnf={all,none}, --last-failed-no-failures={all,none}
|
||||||
|
which tests to run with no previously (known)
|
||||||
|
failures.
|
||||||
|
--sw, --stepwise exit on test failure and continue from last failing
|
||||||
|
test next time
|
||||||
|
--stepwise-skip ignore the first failing test but stop on the next
|
||||||
|
failing test
|
||||||
|
|
||||||
|
reporting:
|
||||||
|
--durations=N show N slowest setup/test durations (N=0 for all).
|
||||||
|
--durations-min=N Minimal duration in seconds for inclusion in slowest
|
||||||
|
list. Default 0.005
|
||||||
|
-v, --verbose increase verbosity.
|
||||||
|
--no-header disable header
|
||||||
|
--no-summary disable summary
|
||||||
|
-q, --quiet decrease verbosity.
|
||||||
|
--verbosity=VERBOSE set verbosity. Default is 0.
|
||||||
|
-r chars show extra test summary info as specified by chars:
|
||||||
|
(f)ailed, (E)rror, (s)kipped, (x)failed, (X)passed,
|
||||||
|
(p)assed, (P)assed with output, (a)ll except passed
|
||||||
|
(p/P), or (A)ll. (w)arnings are enabled by default
|
||||||
|
(see --disable-warnings), 'N' can be used to reset
|
||||||
|
the list. (default: 'fE').
|
||||||
|
--disable-warnings, --disable-pytest-warnings
|
||||||
|
disable warnings summary
|
||||||
|
-l, --showlocals show locals in tracebacks (disabled by default).
|
||||||
|
--tb=style traceback print mode
|
||||||
|
(auto/long/short/line/native/no).
|
||||||
|
--show-capture={no,stdout,stderr,log,all}
|
||||||
|
Controls how captured stdout/stderr/log is shown on
|
||||||
|
failed tests. Default is 'all'.
|
||||||
|
--full-trace don't cut any tracebacks (default is to cut).
|
||||||
|
--color=color color terminal output (yes/no/auto).
|
||||||
|
--code-highlight={yes,no}
|
||||||
|
Whether code should be highlighted (only if --color
|
||||||
|
is also enabled)
|
||||||
|
--pastebin=mode send failed|all info to bpaste.net pastebin service.
|
||||||
|
--junit-xml=path create junit-xml style report file at given path.
|
||||||
|
--junit-prefix=str prepend prefix to classnames in junit-xml output
|
||||||
|
|
||||||
|
pytest-warnings:
|
||||||
|
-W PYTHONWARNINGS, --pythonwarnings=PYTHONWARNINGS
|
||||||
|
set which warnings to report, see -W option of
|
||||||
|
python itself.
|
||||||
|
--maxfail=num exit after first num failures or errors.
|
||||||
|
--strict-config any warnings encountered while parsing the `pytest`
|
||||||
|
section of the configuration file raise errors.
|
||||||
|
--strict-markers, --strict
|
||||||
|
markers not registered in the `markers` section of
|
||||||
|
the configuration file raise errors.
|
||||||
|
-c file load configuration from `file` instead of trying to
|
||||||
|
locate one of the implicit configuration files.
|
||||||
|
--continue-on-collection-errors
|
||||||
|
Force test execution even if collection errors
|
||||||
|
occur.
|
||||||
|
--rootdir=ROOTDIR Define root directory for tests. Can be relative
|
||||||
|
path: 'root_dir', './root_dir',
|
||||||
|
'root_dir/another_dir/'; absolute path:
|
||||||
|
'/home/user/root_dir'; path with variables:
|
||||||
|
'$HOME/root_dir'.
|
||||||
|
|
||||||
|
collection:
|
||||||
|
--collect-only, --co only collect tests, don't execute them.
|
||||||
|
--pyargs try to interpret all arguments as python packages.
|
||||||
|
--ignore=path ignore path during collection (multi-allowed).
|
||||||
|
--ignore-glob=path ignore path pattern during collection (multi-
|
||||||
|
allowed).
|
||||||
|
--deselect=nodeid_prefix
|
||||||
|
deselect item (via node id prefix) during collection
|
||||||
|
(multi-allowed).
|
||||||
|
--confcutdir=dir only load conftest.py's relative to specified dir.
|
||||||
|
--noconftest Don't load any conftest.py files.
|
||||||
|
--keep-duplicates Keep duplicate tests.
|
||||||
|
--collect-in-virtualenv
|
||||||
|
Don't ignore tests in a local virtualenv directory
|
||||||
|
--import-mode={prepend,append,importlib}
|
||||||
|
prepend/append to sys.path when importing test
|
||||||
|
modules and conftest files, default is to prepend.
|
||||||
|
--doctest-modules run doctests in all .py modules
|
||||||
|
--doctest-report={none,cdiff,ndiff,udiff,only_first_failure}
|
||||||
|
choose another output format for diffs on doctest
|
||||||
|
failure
|
||||||
|
--doctest-glob=pat doctests file matching pattern, default: test*.txt
|
||||||
|
--doctest-ignore-import-errors
|
||||||
|
ignore doctest ImportErrors
|
||||||
|
--doctest-continue-on-failure
|
||||||
|
for a given doctest, continue to run after the first
|
||||||
|
failure
|
||||||
|
|
||||||
|
test session debugging and configuration:
|
||||||
|
--basetemp=dir base temporary directory for this test run.(warning:
|
||||||
|
this directory is removed if it exists)
|
||||||
|
-V, --version display pytest version and information about
|
||||||
|
plugins.When given twice, also display information
|
||||||
|
about plugins.
|
||||||
|
-h, --help show help message and configuration info
|
||||||
|
-p name early-load given plugin module name or entry point
|
||||||
|
(multi-allowed).
|
||||||
|
To avoid loading of plugins, use the `no:` prefix,
|
||||||
|
e.g. `no:doctest`.
|
||||||
|
--trace-config trace considerations of conftest.py files.
|
||||||
|
--debug store internal tracing debug information in
|
||||||
|
'pytestdebug.log'.
|
||||||
|
-o OVERRIDE_INI, --override-ini=OVERRIDE_INI
|
||||||
|
override ini option with "option=value" style, e.g.
|
||||||
|
`-o xfail_strict=True -o cache_dir=cache`.
|
||||||
|
--assert=MODE Control assertion debugging tools.
|
||||||
|
'plain' performs no assertion debugging.
|
||||||
|
'rewrite' (the default) rewrites assert statements
|
||||||
|
in test modules on import to provide assert
|
||||||
|
expression information.
|
||||||
|
--setup-only only setup fixtures, do not execute tests.
|
||||||
|
--setup-show show setup of fixtures while executing tests.
|
||||||
|
--setup-plan show what fixtures and tests would be executed but
|
||||||
|
don't execute anything.
|
||||||
|
|
||||||
|
logging:
|
||||||
|
--log-level=LEVEL level of messages to catch/display.
|
||||||
|
Not set by default, so it depends on the root/parent
|
||||||
|
log handler's effective level, where it is "WARNING"
|
||||||
|
by default.
|
||||||
|
--log-format=LOG_FORMAT
|
||||||
|
log format as used by the logging module.
|
||||||
|
--log-date-format=LOG_DATE_FORMAT
|
||||||
|
log date format as used by the logging module.
|
||||||
|
--log-cli-level=LOG_CLI_LEVEL
|
||||||
|
cli logging level.
|
||||||
|
--log-cli-format=LOG_CLI_FORMAT
|
||||||
|
log format as used by the logging module.
|
||||||
|
--log-cli-date-format=LOG_CLI_DATE_FORMAT
|
||||||
|
log date format as used by the logging module.
|
||||||
|
--log-file=LOG_FILE path to a file when logging will be written to.
|
||||||
|
--log-file-level=LOG_FILE_LEVEL
|
||||||
|
log file logging level.
|
||||||
|
--log-file-format=LOG_FILE_FORMAT
|
||||||
|
log format as used by the logging module.
|
||||||
|
--log-file-date-format=LOG_FILE_DATE_FORMAT
|
||||||
|
log date format as used by the logging module.
|
||||||
|
--log-auto-indent=LOG_AUTO_INDENT
|
||||||
|
Auto-indent multiline messages passed to the logging
|
||||||
|
module. Accepts true|on, false|off or an integer.
|
||||||
|
|
||||||
|
[pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:
|
||||||
|
|
||||||
|
markers (linelist): markers for test functions
|
||||||
|
empty_parameter_set_mark (string):
|
||||||
|
default marker for empty parametersets
|
||||||
|
norecursedirs (args): directory patterns to avoid for recursion
|
||||||
|
testpaths (args): directories to search for tests when no files or
|
||||||
|
directories are given in the command line.
|
||||||
|
filterwarnings (linelist):
|
||||||
|
Each line specifies a pattern for
|
||||||
|
warnings.filterwarnings. Processed after
|
||||||
|
-W/--pythonwarnings.
|
||||||
|
usefixtures (args): list of default fixtures to be used with this
|
||||||
|
project
|
||||||
|
python_files (args): glob-style file patterns for Python test module
|
||||||
|
discovery
|
||||||
|
python_classes (args):
|
||||||
|
prefixes or glob names for Python test class
|
||||||
|
discovery
|
||||||
|
python_functions (args):
|
||||||
|
prefixes or glob names for Python test function and
|
||||||
|
method discovery
|
||||||
|
disable_test_id_escaping_and_forfeit_all_rights_to_community_support (bool):
|
||||||
|
disable string escape non-ascii characters, might
|
||||||
|
cause unwanted side effects(use at your own risk)
|
||||||
|
console_output_style (string):
|
||||||
|
console output: "classic", or with additional
|
||||||
|
progress information ("progress" (percentage) |
|
||||||
|
"count").
|
||||||
|
xfail_strict (bool): default for the strict parameter of xfail markers
|
||||||
|
when not given explicitly (default: False)
|
||||||
|
enable_assertion_pass_hook (bool):
|
||||||
|
Enables the pytest_assertion_pass hook.Make sure to
|
||||||
|
delete any previously generated pyc cache files.
|
||||||
|
junit_suite_name (string):
|
||||||
|
Test suite name for JUnit report
|
||||||
|
junit_logging (string):
|
||||||
|
Write captured log messages to JUnit report: one of
|
||||||
|
no|log|system-out|system-err|out-err|all
|
||||||
|
junit_log_passing_tests (bool):
|
||||||
|
Capture log information for passing tests to JUnit
|
||||||
|
report:
|
||||||
|
junit_duration_report (string):
|
||||||
|
Duration time to report: one of total|call
|
||||||
|
junit_family (string):
|
||||||
|
Emit XML for schema: one of legacy|xunit1|xunit2
|
||||||
|
doctest_optionflags (args):
|
||||||
|
option flags for doctests
|
||||||
|
doctest_encoding (string):
|
||||||
|
encoding used for doctest files
|
||||||
|
cache_dir (string): cache directory path.
|
||||||
|
log_level (string): default value for --log-level
|
||||||
|
log_format (string): default value for --log-format
|
||||||
|
log_date_format (string):
|
||||||
|
default value for --log-date-format
|
||||||
|
log_cli (bool): enable log display during test run (also known as
|
||||||
|
"live logging").
|
||||||
|
log_cli_level (string):
|
||||||
|
default value for --log-cli-level
|
||||||
|
log_cli_format (string):
|
||||||
|
default value for --log-cli-format
|
||||||
|
log_cli_date_format (string):
|
||||||
|
default value for --log-cli-date-format
|
||||||
|
log_file (string): default value for --log-file
|
||||||
|
log_file_level (string):
|
||||||
|
default value for --log-file-level
|
||||||
|
log_file_format (string):
|
||||||
|
default value for --log-file-format
|
||||||
|
log_file_date_format (string):
|
||||||
|
default value for --log-file-date-format
|
||||||
|
log_auto_indent (string):
|
||||||
|
default value for --log-auto-indent
|
||||||
|
faulthandler_timeout (string):
|
||||||
|
Dump the traceback of all threads if a test takes
|
||||||
|
more than TIMEOUT seconds to finish.
|
||||||
|
addopts (args): extra command line options
|
||||||
|
minversion (string): minimally required pytest version
|
||||||
|
required_plugins (args):
|
||||||
|
plugins that must be present for pytest to run
|
||||||
|
|
||||||
|
environment variables:
|
||||||
|
PYTEST_ADDOPTS extra command line options
|
||||||
|
PYTEST_PLUGINS comma-separated plugins to load during startup
|
||||||
|
PYTEST_DISABLE_PLUGIN_AUTOLOAD set to disable plugin auto-loading
|
||||||
|
PYTEST_DEBUG set to enable debug tracing of pytest's internals
|
||||||
|
|
||||||
|
|
||||||
|
to see available markers type: pytest --markers
|
||||||
|
to see available fixtures type: pytest --fixtures
|
||||||
|
(shown according to specified file_or_dir or current dir if not specified; fixtures with leading '_' are only shown with the '-v' option
|
||||||
|
|
|
@ -192,8 +192,13 @@ You can override the default temporary directory setting like this:
|
||||||
|
|
||||||
pytest --basetemp=mydir
|
pytest --basetemp=mydir
|
||||||
|
|
||||||
When distributing tests on the local machine, ``pytest`` takes care to
|
.. warning::
|
||||||
configure a basetemp directory for the sub processes such that all temporary
|
|
||||||
|
The contents of ``mydir`` will be completely removed, so make sure to use a directory
|
||||||
|
for that purpose only.
|
||||||
|
|
||||||
|
When distributing tests on the local machine using ``pytest-xdist``, care is taken to
|
||||||
|
automatically configure a basetemp directory for the sub processes such that all temporary
|
||||||
data lands below a single per-test run basetemp directory.
|
data lands below a single per-test run basetemp directory.
|
||||||
|
|
||||||
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
|
.. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html
|
||||||
|
|
|
@ -57,6 +57,8 @@ Getting help on version, option names, environment variables
|
||||||
pytest -h | --help # show help on command line and config file options
|
pytest -h | --help # show help on command line and config file options
|
||||||
|
|
||||||
|
|
||||||
|
The full command-line flags can be found in the :ref:`reference <command-line-flags>`.
|
||||||
|
|
||||||
.. _maxfail:
|
.. _maxfail:
|
||||||
|
|
||||||
Stopping after the first (or N) failures
|
Stopping after the first (or N) failures
|
||||||
|
|
|
@ -10,7 +10,7 @@ build-backend = "setuptools.build_meta"
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
minversion = "2.0"
|
minversion = "2.0"
|
||||||
addopts = "-rfEX -p pytester --strict-markers"
|
addopts = "-rfEX -p pytester --strict-markers"
|
||||||
python_files = ["test_*.py", "*_test.py", "testing/*/*.py"]
|
python_files = ["test_*.py", "*_test.py", "testing/python/*.py"]
|
||||||
python_classes = ["Test", "Acceptance"]
|
python_classes = ["Test", "Acceptance"]
|
||||||
python_functions = ["test"]
|
python_functions = ["test"]
|
||||||
# NOTE: "doc" is not included here, but gets tested explicitly via "doctesting".
|
# NOTE: "doc" is not included here, but gets tested explicitly via "doctesting".
|
||||||
|
|
|
@ -3,23 +3,20 @@ pytest-{version}
|
||||||
|
|
||||||
The pytest team is proud to announce the {version} release!
|
The pytest team is proud to announce the {version} release!
|
||||||
|
|
||||||
pytest is a mature Python testing tool with more than a 2000 tests
|
This release contains new features, improvements, bug fixes, and breaking changes, so users
|
||||||
against itself, passing on many different interpreters and platforms.
|
are encouraged to take a look at the CHANGELOG carefully:
|
||||||
|
|
||||||
This release contains a number of bug fixes and improvements, so users are encouraged
|
https://docs.pytest.org/en/stable/changelog.html
|
||||||
to take a look at the CHANGELOG:
|
|
||||||
|
|
||||||
https://docs.pytest.org/en/latest/changelog.html
|
|
||||||
|
|
||||||
For complete documentation, please visit:
|
For complete documentation, please visit:
|
||||||
|
|
||||||
https://docs.pytest.org/en/latest/
|
https://docs.pytest.org/en/stable/
|
||||||
|
|
||||||
As usual, you can upgrade from PyPI via:
|
As usual, you can upgrade from PyPI via:
|
||||||
|
|
||||||
pip install -U pytest
|
pip install -U pytest
|
||||||
|
|
||||||
Thanks to all who contributed to this release, among them:
|
Thanks to all of the contributors to this release:
|
||||||
|
|
||||||
{contributors}
|
{contributors}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||||
|
|
||||||
pip install --upgrade pytest
|
pip install --upgrade pytest
|
||||||
|
|
||||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||||
|
|
||||||
Thanks to all who contributed to this release, among them:
|
Thanks to all of the contributors to this release:
|
||||||
|
|
||||||
{contributors}
|
{contributors}
|
||||||
|
|
||||||
|
|
|
@ -246,10 +246,20 @@ class TracebackEntry:
|
||||||
|
|
||||||
Mostly for internal use.
|
Mostly for internal use.
|
||||||
"""
|
"""
|
||||||
f = self.frame
|
tbh = (
|
||||||
tbh = f.f_locals.get(
|
False
|
||||||
"__tracebackhide__", f.f_globals.get("__tracebackhide__", False)
|
|
||||||
) # type: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]]
|
) # type: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]]
|
||||||
|
for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals):
|
||||||
|
# in normal cases, f_locals and f_globals are dictionaries
|
||||||
|
# however via `exec(...)` / `eval(...)` they can be other types
|
||||||
|
# (even incorrect types!).
|
||||||
|
# as such, we suppress all exceptions while accessing __tracebackhide__
|
||||||
|
try:
|
||||||
|
tbh = maybe_ns_dct["__tracebackhide__"]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
break
|
||||||
if tbh and callable(tbh):
|
if tbh and callable(tbh):
|
||||||
return tbh(None if self._excinfo is None else self._excinfo())
|
return tbh(None if self._excinfo is None else self._excinfo())
|
||||||
return tbh
|
return tbh
|
||||||
|
@ -1038,25 +1048,21 @@ class ReprEntry(TerminalRepr):
|
||||||
# such as "> assert 0"
|
# such as "> assert 0"
|
||||||
fail_marker = "{} ".format(FormattedExcinfo.fail_marker)
|
fail_marker = "{} ".format(FormattedExcinfo.fail_marker)
|
||||||
indent_size = len(fail_marker)
|
indent_size = len(fail_marker)
|
||||||
indents = []
|
indents = [] # type: List[str]
|
||||||
source_lines = []
|
source_lines = [] # type: List[str]
|
||||||
failure_lines = []
|
failure_lines = [] # type: List[str]
|
||||||
seeing_failures = False
|
for index, line in enumerate(self.lines):
|
||||||
for line in self.lines:
|
is_failure_line = line.startswith(fail_marker)
|
||||||
is_source_line = not line.startswith(fail_marker)
|
if is_failure_line:
|
||||||
if is_source_line:
|
# from this point on all lines are considered part of the failure
|
||||||
assert not seeing_failures, (
|
failure_lines.extend(self.lines[index:])
|
||||||
"Unexpected failure lines between source lines:\n"
|
break
|
||||||
+ "\n".join(self.lines)
|
else:
|
||||||
)
|
|
||||||
if self.style == "value":
|
if self.style == "value":
|
||||||
source_lines.append(line)
|
source_lines.append(line)
|
||||||
else:
|
else:
|
||||||
indents.append(line[:indent_size])
|
indents.append(line[:indent_size])
|
||||||
source_lines.append(line[indent_size:])
|
source_lines.append(line[indent_size:])
|
||||||
else:
|
|
||||||
seeing_failures = True
|
|
||||||
failure_lines.append(line)
|
|
||||||
|
|
||||||
tw._write_source(source_lines, indents)
|
tw._write_source(source_lines, indents)
|
||||||
|
|
||||||
|
|
|
@ -694,13 +694,18 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
return
|
return
|
||||||
expect_docstring = False
|
expect_docstring = False
|
||||||
elif (
|
elif (
|
||||||
not isinstance(item, ast.ImportFrom)
|
isinstance(item, ast.ImportFrom)
|
||||||
or item.level > 0
|
and item.level == 0
|
||||||
or item.module != "__future__"
|
and item.module == "__future__"
|
||||||
):
|
):
|
||||||
lineno = item.lineno
|
pass
|
||||||
|
else:
|
||||||
break
|
break
|
||||||
pos += 1
|
pos += 1
|
||||||
|
# Special case: for a decorated function, set the lineno to that of the
|
||||||
|
# first decorator, not the `def`. Issue #4984.
|
||||||
|
if isinstance(item, ast.FunctionDef) and item.decorator_list:
|
||||||
|
lineno = item.decorator_list[0].lineno
|
||||||
else:
|
else:
|
||||||
lineno = item.lineno
|
lineno = item.lineno
|
||||||
imports = [
|
imports = [
|
||||||
|
|
|
@ -537,7 +537,7 @@ class MultiCapture:
|
||||||
self._in_suspended = True
|
self._in_suspended = True
|
||||||
|
|
||||||
def resume_capturing(self) -> None:
|
def resume_capturing(self) -> None:
|
||||||
self._state = "resumed"
|
self._state = "started"
|
||||||
if self.out:
|
if self.out:
|
||||||
self.out.resume()
|
self.out.resume()
|
||||||
if self.err:
|
if self.err:
|
||||||
|
@ -558,6 +558,10 @@ class MultiCapture:
|
||||||
if self.in_:
|
if self.in_:
|
||||||
self.in_.done()
|
self.in_.done()
|
||||||
|
|
||||||
|
def is_started(self) -> bool:
|
||||||
|
"""Whether actively capturing -- not suspended or stopped."""
|
||||||
|
return self._state == "started"
|
||||||
|
|
||||||
def readouterr(self) -> CaptureResult:
|
def readouterr(self) -> CaptureResult:
|
||||||
if self.out:
|
if self.out:
|
||||||
out = self.out.snap()
|
out = self.out.snap()
|
||||||
|
@ -697,11 +701,19 @@ class CaptureManager:
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def global_and_fixture_disabled(self) -> Generator[None, None, None]:
|
def global_and_fixture_disabled(self) -> Generator[None, None, None]:
|
||||||
"""Context manager to temporarily disable global and current fixture capturing."""
|
"""Context manager to temporarily disable global and current fixture capturing."""
|
||||||
self.suspend()
|
do_fixture = self._capture_fixture and self._capture_fixture._is_started()
|
||||||
|
if do_fixture:
|
||||||
|
self.suspend_fixture()
|
||||||
|
do_global = self._global_capturing and self._global_capturing.is_started()
|
||||||
|
if do_global:
|
||||||
|
self.suspend_global_capture()
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
self.resume()
|
if do_global:
|
||||||
|
self.resume_global_capture()
|
||||||
|
if do_fixture:
|
||||||
|
self.resume_fixture()
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def item_capture(self, when: str, item: Item) -> Generator[None, None, None]:
|
def item_capture(self, when: str, item: Item) -> Generator[None, None, None]:
|
||||||
|
@ -810,6 +822,12 @@ class CaptureFixture:
|
||||||
if self._capture is not None:
|
if self._capture is not None:
|
||||||
self._capture.resume_capturing()
|
self._capture.resume_capturing()
|
||||||
|
|
||||||
|
def _is_started(self) -> bool:
|
||||||
|
"""Whether actively capturing -- not disabled or closed."""
|
||||||
|
if self._capture is not None:
|
||||||
|
return self._capture.is_started()
|
||||||
|
return False
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def disabled(self) -> Generator[None, None, None]:
|
def disabled(self) -> Generator[None, None, None]:
|
||||||
"""Temporarily disables capture while inside the 'with' block."""
|
"""Temporarily disables capture while inside the 'with' block."""
|
||||||
|
|
|
@ -1174,9 +1174,8 @@ class Config:
|
||||||
missing_plugins.append(required_plugin)
|
missing_plugins.append(required_plugin)
|
||||||
|
|
||||||
if missing_plugins:
|
if missing_plugins:
|
||||||
fail(
|
raise UsageError(
|
||||||
"Missing required plugins: {}".format(", ".join(missing_plugins)),
|
"Missing required plugins: {}".format(", ".join(missing_plugins)),
|
||||||
pytrace=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def _warn_or_fail_if_strict(self, message: str) -> None:
|
def _warn_or_fail_if_strict(self, message: str) -> None:
|
||||||
|
|
|
@ -170,6 +170,8 @@ def showhelp(config: Config) -> None:
|
||||||
help, type, default = config._parser._inidict[name]
|
help, type, default = config._parser._inidict[name]
|
||||||
if type is None:
|
if type is None:
|
||||||
type = "string"
|
type = "string"
|
||||||
|
if help is None:
|
||||||
|
raise TypeError("help argument cannot be None for {}".format(name))
|
||||||
spec = "{} ({}):".format(name, type)
|
spec = "{} ({}):".format(name, type)
|
||||||
tw.write(" %s" % spec)
|
tw.write(" %s" % spec)
|
||||||
spec_len = len(spec)
|
spec_len = len(spec)
|
||||||
|
@ -191,6 +193,7 @@ def showhelp(config: Config) -> None:
|
||||||
tw.write(" " * (indent_len - spec_len - 2))
|
tw.write(" " * (indent_len - spec_len - 2))
|
||||||
wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False)
|
wrapped = textwrap.wrap(help, columns - indent_len, break_on_hyphens=False)
|
||||||
|
|
||||||
|
if wrapped:
|
||||||
tw.line(wrapped[0])
|
tw.line(wrapped[0])
|
||||||
for line in wrapped[1:]:
|
for line in wrapped[1:]:
|
||||||
tw.line(indent + line)
|
tw.line(indent + line)
|
||||||
|
|
|
@ -377,6 +377,12 @@ def record_testsuite_property(request: FixtureRequest) -> Callable[[str, object]
|
||||||
record_testsuite_property("STORAGE_TYPE", "CEPH")
|
record_testsuite_property("STORAGE_TYPE", "CEPH")
|
||||||
|
|
||||||
``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped.
|
``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Currently this fixture **does not work** with the
|
||||||
|
`pytest-xdist <https://github.com/pytest-dev/pytest-xdist>`__ plugin. See issue
|
||||||
|
`#7767 <https://github.com/pytest-dev/pytest/issues/7767>`__ for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
|
|
|
@ -437,6 +437,7 @@ class LogCaptureFixture:
|
||||||
# save the original log-level to restore it during teardown
|
# save the original log-level to restore it during teardown
|
||||||
self._initial_logger_levels.setdefault(logger, logger_obj.level)
|
self._initial_logger_levels.setdefault(logger, logger_obj.level)
|
||||||
logger_obj.setLevel(level)
|
logger_obj.setLevel(level)
|
||||||
|
if self._initial_handler_level is None:
|
||||||
self._initial_handler_level = self.handler.level
|
self._initial_handler_level = self.handler.level
|
||||||
self.handler.setLevel(level)
|
self.handler.setLevel(level)
|
||||||
|
|
||||||
|
|
|
@ -604,7 +604,10 @@ class FSCollector(Collector):
|
||||||
|
|
||||||
|
|
||||||
class File(FSCollector):
|
class File(FSCollector):
|
||||||
""" base class for collecting tests from a file. """
|
"""Base class for collecting tests from a file.
|
||||||
|
|
||||||
|
:ref:`non-python tests`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Item(Node):
|
class Item(Node):
|
||||||
|
|
|
@ -162,9 +162,10 @@ def async_warn_and_skip(nodeid: str) -> None:
|
||||||
msg += (
|
msg += (
|
||||||
"You need to install a suitable plugin for your async framework, for example:\n"
|
"You need to install a suitable plugin for your async framework, for example:\n"
|
||||||
)
|
)
|
||||||
|
msg += " - anyio\n"
|
||||||
msg += " - pytest-asyncio\n"
|
msg += " - pytest-asyncio\n"
|
||||||
msg += " - pytest-trio\n"
|
|
||||||
msg += " - pytest-tornasync\n"
|
msg += " - pytest-tornasync\n"
|
||||||
|
msg += " - pytest-trio\n"
|
||||||
msg += " - pytest-twisted"
|
msg += " - pytest-twisted"
|
||||||
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))
|
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))
|
||||||
skip(msg="async def function and no async plugin installed (see warnings)")
|
skip(msg="async def function and no async plugin installed (see warnings)")
|
||||||
|
@ -1254,6 +1255,9 @@ def _idval(
|
||||||
return str(val)
|
return str(val)
|
||||||
elif isinstance(val, REGEX_TYPE):
|
elif isinstance(val, REGEX_TYPE):
|
||||||
return ascii_escaped(val.pattern)
|
return ascii_escaped(val.pattern)
|
||||||
|
elif val is NOTSET:
|
||||||
|
# Fallback to default. Note that NOTSET is an enum.Enum.
|
||||||
|
pass
|
||||||
elif isinstance(val, enum.Enum):
|
elif isinstance(val, enum.Enum):
|
||||||
return str(val)
|
return str(val)
|
||||||
elif isinstance(getattr(val, "__name__", None), str):
|
elif isinstance(getattr(val, "__name__", None), str):
|
||||||
|
|
|
@ -4,6 +4,8 @@ import os
|
||||||
import queue
|
import queue
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
from typing import Any
|
||||||
|
from typing import Dict
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
@ -1045,28 +1047,34 @@ raise ValueError()
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"reproptions",
|
"reproptions",
|
||||||
[
|
[
|
||||||
|
pytest.param(
|
||||||
{
|
{
|
||||||
"style": style,
|
"style": style,
|
||||||
"showlocals": showlocals,
|
"showlocals": showlocals,
|
||||||
"funcargs": funcargs,
|
"funcargs": funcargs,
|
||||||
"tbfilter": tbfilter,
|
"tbfilter": tbfilter,
|
||||||
}
|
},
|
||||||
for style in ("long", "short", "no")
|
id="style={},showlocals={},funcargs={},tbfilter={}".format(
|
||||||
|
style, showlocals, funcargs, tbfilter
|
||||||
|
),
|
||||||
|
)
|
||||||
|
for style in ["long", "short", "line", "no", "native", "value", "auto"]
|
||||||
for showlocals in (True, False)
|
for showlocals in (True, False)
|
||||||
for tbfilter in (True, False)
|
for tbfilter in (True, False)
|
||||||
for funcargs in (True, False)
|
for funcargs in (True, False)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_format_excinfo(self, importasmod, reproptions):
|
def test_format_excinfo(self, reproptions: Dict[str, Any]) -> None:
|
||||||
mod = importasmod(
|
def bar():
|
||||||
"""
|
assert False, "some error"
|
||||||
def g(x):
|
|
||||||
raise ValueError(x)
|
def foo():
|
||||||
def f():
|
bar()
|
||||||
g(3)
|
|
||||||
"""
|
# using inline functions as opposed to importasmod so we get source code lines
|
||||||
)
|
# in the tracebacks (otherwise getinspect doesn't find the source code).
|
||||||
excinfo = pytest.raises(ValueError, mod.f)
|
with pytest.raises(AssertionError) as excinfo:
|
||||||
|
foo()
|
||||||
file = io.StringIO()
|
file = io.StringIO()
|
||||||
tw = TerminalWriter(file=file)
|
tw = TerminalWriter(file=file)
|
||||||
repr = excinfo.getrepr(**reproptions)
|
repr = excinfo.getrepr(**reproptions)
|
||||||
|
@ -1344,6 +1352,19 @@ raise ValueError()
|
||||||
)
|
)
|
||||||
assert out == expected_out
|
assert out == expected_out
|
||||||
|
|
||||||
|
def test_exec_type_error_filter(self, importasmod):
|
||||||
|
"""See #7742"""
|
||||||
|
mod = importasmod(
|
||||||
|
"""\
|
||||||
|
def f():
|
||||||
|
exec("a = 1", {}, [])
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
with pytest.raises(TypeError) as excinfo:
|
||||||
|
mod.f()
|
||||||
|
# previously crashed with `AttributeError: list has no attribute get`
|
||||||
|
excinfo.traceback.filter()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("style", ["short", "long"])
|
@pytest.mark.parametrize("style", ["short", "long"])
|
||||||
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])
|
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])
|
||||||
|
|
|
@ -65,6 +65,7 @@ def test_change_level_undos_handler_level(testdir: Testdir) -> None:
|
||||||
|
|
||||||
def test1(caplog):
|
def test1(caplog):
|
||||||
assert caplog.handler.level == 0
|
assert caplog.handler.level == 0
|
||||||
|
caplog.set_level(9999)
|
||||||
caplog.set_level(41)
|
caplog.set_level(41)
|
||||||
assert caplog.handler.level == 41
|
assert caplog.handler.level == 41
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
*.html
|
||||||
|
assets/
|
|
@ -0,0 +1,13 @@
|
||||||
|
This folder contains tests and support files for smoke testing popular plugins against the current pytest version.
|
||||||
|
|
||||||
|
The objective is to gauge if any intentional or unintentional changes in pytest break plugins.
|
||||||
|
|
||||||
|
As a rule of thumb, we should add plugins here:
|
||||||
|
|
||||||
|
1. That are used at large. This might be subjective in some cases, but if answer is yes to
|
||||||
|
the question: *if a new release of pytest causes pytest-X to break, will this break a ton of test suites out there?*.
|
||||||
|
2. That don't have large external dependencies: such as external services.
|
||||||
|
|
||||||
|
Besides adding the plugin as dependency, we should also add a quick test which uses some
|
||||||
|
minimal part of the plugin, a smoke test. Also consider reusing one of the existing tests if that's
|
||||||
|
possible.
|
|
@ -0,0 +1,9 @@
|
||||||
|
Feature: Buy things with apple
|
||||||
|
|
||||||
|
Scenario: Buy fruits
|
||||||
|
Given A wallet with 50
|
||||||
|
|
||||||
|
When I buy some apples for 1
|
||||||
|
And I buy some bananas for 2
|
||||||
|
|
||||||
|
Then I have 47 left
|
|
@ -0,0 +1,39 @@
|
||||||
|
from pytest_bdd import given
|
||||||
|
from pytest_bdd import scenario
|
||||||
|
from pytest_bdd import then
|
||||||
|
from pytest_bdd import when
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@scenario("bdd_wallet.feature", "Buy fruits")
|
||||||
|
def test_publish():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def wallet():
|
||||||
|
class Wallet:
|
||||||
|
amount = 0
|
||||||
|
|
||||||
|
return Wallet()
|
||||||
|
|
||||||
|
|
||||||
|
@given("A wallet with 50")
|
||||||
|
def fill_wallet(wallet):
|
||||||
|
wallet.amount = 50
|
||||||
|
|
||||||
|
|
||||||
|
@when("I buy some apples for 1")
|
||||||
|
def buy_apples(wallet):
|
||||||
|
wallet.amount -= 1
|
||||||
|
|
||||||
|
|
||||||
|
@when("I buy some bananas for 2")
|
||||||
|
def buy_bananas(wallet):
|
||||||
|
wallet.amount -= 2
|
||||||
|
|
||||||
|
|
||||||
|
@then("I have 47 left")
|
||||||
|
def check(wallet):
|
||||||
|
assert wallet.amount == 47
|
|
@ -0,0 +1 @@
|
||||||
|
SECRET_KEY = "mysecret"
|
|
@ -0,0 +1,4 @@
|
||||||
|
[pytest]
|
||||||
|
addopts = --strict-markers
|
||||||
|
filterwarnings =
|
||||||
|
error::pytest.PytestWarning
|
|
@ -0,0 +1,8 @@
|
||||||
|
import anyio
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.anyio
|
||||||
|
async def test_sleep():
|
||||||
|
await anyio.sleep(0)
|
|
@ -0,0 +1,8 @@
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_sleep():
|
||||||
|
await asyncio.sleep(0)
|
|
@ -0,0 +1,2 @@
|
||||||
|
def test_mocker(mocker):
|
||||||
|
mocker.MagicMock()
|
|
@ -0,0 +1,8 @@
|
||||||
|
import trio
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.trio
|
||||||
|
async def test_sleep():
|
||||||
|
await trio.sleep(0)
|
|
@ -0,0 +1,18 @@
|
||||||
|
import pytest_twisted
|
||||||
|
from twisted.internet.task import deferLater
|
||||||
|
|
||||||
|
|
||||||
|
def sleep():
|
||||||
|
import twisted.internet.reactor
|
||||||
|
|
||||||
|
return deferLater(clock=twisted.internet.reactor, delay=0)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest_twisted.inlineCallbacks
|
||||||
|
def test_inlineCallbacks():
|
||||||
|
yield sleep()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest_twisted.ensureDeferred
|
||||||
|
async def test_inlineCallbacks_async():
|
||||||
|
await sleep()
|
|
@ -0,0 +1,10 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def test_foo():
|
||||||
|
assert True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("i", range(3))
|
||||||
|
def test_bar(i):
|
||||||
|
assert True
|
|
@ -19,6 +19,7 @@ from hypothesis import strategies
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import fixtures
|
from _pytest import fixtures
|
||||||
from _pytest import python
|
from _pytest import python
|
||||||
|
from _pytest.compat import NOTSET
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.pytester import Testdir
|
from _pytest.pytester import Testdir
|
||||||
from _pytest.python import _idval
|
from _pytest.python import _idval
|
||||||
|
@ -363,6 +364,14 @@ class TestMetafunc:
|
||||||
for val, expected in values:
|
for val, expected in values:
|
||||||
assert _idval(val, "a", 6, None, nodeid=None, config=None) == expected
|
assert _idval(val, "a", 6, None, nodeid=None, config=None) == expected
|
||||||
|
|
||||||
|
def test_notset_idval(self) -> None:
|
||||||
|
"""Test that a NOTSET value (used by an empty parameterset) generates
|
||||||
|
a proper ID.
|
||||||
|
|
||||||
|
Regression test for #7686.
|
||||||
|
"""
|
||||||
|
assert _idval(NOTSET, "a", 0, None, nodeid=None, config=None) == "a0"
|
||||||
|
|
||||||
def test_idmaker_autoname(self) -> None:
|
def test_idmaker_autoname(self) -> None:
|
||||||
"""#250"""
|
"""#250"""
|
||||||
result = idmaker(
|
result = idmaker(
|
||||||
|
|
|
@ -992,7 +992,7 @@ class TestAssertionRewriteHookDetails:
|
||||||
e = OSError()
|
e = OSError()
|
||||||
e.errno = 10
|
e.errno = 10
|
||||||
raise e
|
raise e
|
||||||
yield
|
yield # type:ignore[unreachable]
|
||||||
|
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
_pytest.assertion.rewrite, "atomic_write", atomic_write_failed
|
_pytest.assertion.rewrite, "atomic_write", atomic_write_failed
|
||||||
|
@ -1600,7 +1600,7 @@ class TestPyCacheDir:
|
||||||
if prefix:
|
if prefix:
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8):
|
||||||
pytest.skip("pycache_prefix not available in py<38")
|
pytest.skip("pycache_prefix not available in py<38")
|
||||||
monkeypatch.setattr(sys, "pycache_prefix", prefix)
|
monkeypatch.setattr(sys, "pycache_prefix", prefix) # type:ignore
|
||||||
|
|
||||||
assert get_cache_dir(Path(source)) == Path(expected)
|
assert get_cache_dir(Path(source)) == Path(expected)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ from _pytest.capture import _get_multicapture
|
||||||
from _pytest.capture import CaptureManager
|
from _pytest.capture import CaptureManager
|
||||||
from _pytest.capture import MultiCapture
|
from _pytest.capture import MultiCapture
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
|
from _pytest.pytester import Testdir
|
||||||
|
|
||||||
# note: py.io capture tests where copied from
|
# note: py.io capture tests where copied from
|
||||||
# pylib 1.4.20.dev2 (rev 13d9af95547e)
|
# pylib 1.4.20.dev2 (rev 13d9af95547e)
|
||||||
|
@ -633,6 +634,34 @@ class TestCaptureFixture:
|
||||||
else:
|
else:
|
||||||
result.stdout.no_fnmatch_line("*test_normal executed*")
|
result.stdout.no_fnmatch_line("*test_normal executed*")
|
||||||
|
|
||||||
|
def test_disabled_capture_fixture_twice(self, testdir: Testdir) -> None:
|
||||||
|
"""Test that an inner disabled() exit doesn't undo an outer disabled().
|
||||||
|
|
||||||
|
Issue #7148.
|
||||||
|
"""
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
def test_disabled(capfd):
|
||||||
|
print('captured before')
|
||||||
|
with capfd.disabled():
|
||||||
|
print('while capture is disabled 1')
|
||||||
|
with capfd.disabled():
|
||||||
|
print('while capture is disabled 2')
|
||||||
|
print('while capture is disabled 1 after')
|
||||||
|
print('captured after')
|
||||||
|
assert capfd.readouterr() == ('captured before\\ncaptured after\\n', '')
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest_subprocess()
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
"*while capture is disabled 1",
|
||||||
|
"*while capture is disabled 2",
|
||||||
|
"*while capture is disabled 1 after",
|
||||||
|
],
|
||||||
|
consecutive=True,
|
||||||
|
)
|
||||||
|
|
||||||
@pytest.mark.parametrize("fixture", ["capsys", "capfd"])
|
@pytest.mark.parametrize("fixture", ["capsys", "capfd"])
|
||||||
def test_fixture_use_by_other_fixtures(self, testdir, fixture):
|
def test_fixture_use_by_other_fixtures(self, testdir, fixture):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1427,3 +1427,17 @@ class TestImportModeImportlib:
|
||||||
"* 1 failed in *",
|
"* 1 failed in *",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_does_not_crash_on_error_from_decorated_function(testdir: Testdir) -> None:
|
||||||
|
"""Regression test for an issue around bad exception formatting due to
|
||||||
|
assertion rewriting mangling lineno's (#4984)."""
|
||||||
|
testdir.makepyfile(
|
||||||
|
"""
|
||||||
|
@pytest.fixture
|
||||||
|
def a(): return 4
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest()
|
||||||
|
# Not INTERNAL_ERROR
|
||||||
|
assert result.ret == ExitCode.INTERRUPTED
|
||||||
|
|
|
@ -362,11 +362,30 @@ class TestParseIni:
|
||||||
testdir.makeini(ini_file_text)
|
testdir.makeini(ini_file_text)
|
||||||
|
|
||||||
if exception_text:
|
if exception_text:
|
||||||
with pytest.raises(pytest.fail.Exception, match=exception_text):
|
with pytest.raises(pytest.UsageError, match=exception_text):
|
||||||
testdir.parseconfig()
|
testdir.parseconfig()
|
||||||
else:
|
else:
|
||||||
testdir.parseconfig()
|
testdir.parseconfig()
|
||||||
|
|
||||||
|
def test_early_config_cmdline(self, testdir, monkeypatch):
|
||||||
|
"""early_config contains options registered by third-party plugins.
|
||||||
|
|
||||||
|
This is a regression involving pytest-cov (and possibly others) introduced in #7700.
|
||||||
|
"""
|
||||||
|
testdir.makepyfile(
|
||||||
|
myplugin="""
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addoption('--foo', default=None, dest='foo')
|
||||||
|
|
||||||
|
def pytest_load_initial_conftests(early_config, parser, args):
|
||||||
|
assert early_config.known_args_namespace.foo == "1"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
monkeypatch.setenv("PYTEST_PLUGINS", "myplugin")
|
||||||
|
testdir.syspathinsert()
|
||||||
|
result = testdir.runpytest("--foo=1")
|
||||||
|
result.stdout.fnmatch_lines("* no tests ran in *")
|
||||||
|
|
||||||
|
|
||||||
class TestConfigCmdlineParsing:
|
class TestConfigCmdlineParsing:
|
||||||
def test_parsing_again_fails(self, testdir):
|
def test_parsing_again_fails(self, testdir):
|
||||||
|
|
|
@ -38,6 +38,41 @@ def test_help(testdir):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_none_help_param_raises_exception(testdir):
|
||||||
|
"""Tests a None help param raises a TypeError.
|
||||||
|
"""
|
||||||
|
testdir.makeconftest(
|
||||||
|
"""
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addini("test_ini", None, default=True, type="bool")
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest("--help")
|
||||||
|
result.stderr.fnmatch_lines(
|
||||||
|
["*TypeError: help argument cannot be None for test_ini*"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_help_param(testdir):
|
||||||
|
"""Tests an empty help param is displayed correctly.
|
||||||
|
"""
|
||||||
|
testdir.makeconftest(
|
||||||
|
"""
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addini("test_ini", "", default=True, type="bool")
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
result = testdir.runpytest("--help")
|
||||||
|
assert result.ret == 0
|
||||||
|
lines = [
|
||||||
|
" required_plugins (args):",
|
||||||
|
" plugins that must be present for pytest to run*",
|
||||||
|
" test_ini (bool):*",
|
||||||
|
"environment variables:",
|
||||||
|
]
|
||||||
|
result.stdout.fnmatch_lines(lines, consecutive=True)
|
||||||
|
|
||||||
|
|
||||||
def test_hookvalidation_unknown(testdir):
|
def test_hookvalidation_unknown(testdir):
|
||||||
testdir.makeconftest(
|
testdir.makeconftest(
|
||||||
"""
|
"""
|
||||||
|
|
38
tox.ini
38
tox.ini
|
@ -13,6 +13,7 @@ envlist =
|
||||||
pypy3
|
pypy3
|
||||||
py37-{pexpect,xdist,unittestextras,numpy,pluggymaster}
|
py37-{pexpect,xdist,unittestextras,numpy,pluggymaster}
|
||||||
doctesting
|
doctesting
|
||||||
|
plugins
|
||||||
py37-freeze
|
py37-freeze
|
||||||
docs
|
docs
|
||||||
docs-checklinks
|
docs-checklinks
|
||||||
|
@ -114,6 +115,43 @@ commands =
|
||||||
rm -rf {envdir}/.pytest_cache
|
rm -rf {envdir}/.pytest_cache
|
||||||
make regen
|
make regen
|
||||||
|
|
||||||
|
[testenv:plugins]
|
||||||
|
# use latest versions of all plugins, including pre-releases
|
||||||
|
pip_pre=true
|
||||||
|
# use latest pip and new dependency resolver (#7783)
|
||||||
|
download=true
|
||||||
|
install_command=python -m pip --use-feature=2020-resolver install {opts} {packages}
|
||||||
|
changedir = testing/plugins_integration
|
||||||
|
deps =
|
||||||
|
anyio[curio,trio]
|
||||||
|
django
|
||||||
|
pytest-asyncio
|
||||||
|
pytest-bdd
|
||||||
|
pytest-cov
|
||||||
|
pytest-django
|
||||||
|
pytest-flakes
|
||||||
|
pytest-html
|
||||||
|
pytest-mock
|
||||||
|
pytest-sugar
|
||||||
|
pytest-trio
|
||||||
|
pytest-twisted
|
||||||
|
twisted
|
||||||
|
pytest-xvfb
|
||||||
|
setenv =
|
||||||
|
PYTHONPATH=.
|
||||||
|
commands =
|
||||||
|
pip check
|
||||||
|
pytest bdd_wallet.py
|
||||||
|
pytest --cov=. simple_integration.py
|
||||||
|
pytest --ds=django_settings simple_integration.py
|
||||||
|
pytest --html=simple.html simple_integration.py
|
||||||
|
pytest pytest_anyio_integration.py
|
||||||
|
pytest pytest_asyncio_integration.py
|
||||||
|
pytest pytest_mock_integration.py
|
||||||
|
pytest pytest_trio_integration.py
|
||||||
|
pytest pytest_twisted_integration.py
|
||||||
|
pytest simple_integration.py --force-sugar --flakes
|
||||||
|
|
||||||
[testenv:py37-freeze]
|
[testenv:py37-freeze]
|
||||||
changedir = testing/freeze
|
changedir = testing/freeze
|
||||||
deps =
|
deps =
|
||||||
|
|
Loading…
Reference in New Issue