Merge branch 'features' into integrate-pytest-warnings

This commit is contained in:
Bruno Oliveira 2017-02-18 11:03:15 -02:00
commit a7643a5fbe
129 changed files with 1739 additions and 946 deletions

View File

@ -29,9 +29,11 @@ env:
- TESTENV=docs - TESTENV=docs
matrix: matrix:
allow_failures: include:
# py35-trial failing on Linux: #1989 - env: TESTENV=py36
- env: TESTENV=py35-trial python: '3.6-dev'
- env: TESTENV=py37
python: 'nightly'
script: tox --recreate -e $TESTENV script: tox --recreate -e $TESTENV

12
AUTHORS
View File

@ -44,9 +44,11 @@ David Mohr
David Vierra David Vierra
Diego Russo Diego Russo
Dmitry Dygalo Dmitry Dygalo
Duncan Betts
Edison Gustavo Muenz Edison Gustavo Muenz
Edoardo Batini Edoardo Batini
Eduardo Schettino Eduardo Schettino
Eli Boyarski
Elizaveta Shashkova Elizaveta Shashkova
Endre Galaczi Endre Galaczi
Eric Hunsberger Eric Hunsberger
@ -70,6 +72,7 @@ Janne Vanhala
Jason R. Coombs Jason R. Coombs
Javier Domingo Cansino Javier Domingo Cansino
Javier Romero Javier Romero
Jeff Widman
John Towler John Towler
Jon Sonesen Jon Sonesen
Jordan Guymon Jordan Guymon
@ -81,9 +84,13 @@ Katarzyna Jachim
Kevin Cox Kevin Cox
Lee Kamentsky Lee Kamentsky
Lev Maximov Lev Maximov
Loic Esteve
Lukas Bednar Lukas Bednar
Luke Murphy
Maciek Fijalkowski Maciek Fijalkowski
Maho Maho
Mandeep Bhutani
Manuel Krebber
Marc Schlaich Marc Schlaich
Marcin Bachry Marcin Bachry
Mark Abramowitz Mark Abramowitz
@ -101,7 +108,10 @@ Michael Aquilina
Michael Birtwell Michael Birtwell
Michael Droettboom Michael Droettboom
Michael Seifert Michael Seifert
Michal Wajszczuk
Mike Lundy Mike Lundy
Ned Batchelder
Neven Mundar
Nicolas Delaby Nicolas Delaby
Oleg Pidsadnyi Oleg Pidsadnyi
Oliver Bestwalter Oliver Bestwalter
@ -113,6 +123,7 @@ Quentin Pradet
Ralf Schmitt Ralf Schmitt
Raphael Pierzina Raphael Pierzina
Raquel Alegre Raquel Alegre
Ravi Chandra
Roberto Polli Roberto Polli
Romain Dorgueil Romain Dorgueil
Roman Bolshakov Roman Bolshakov
@ -134,6 +145,7 @@ Tom Viner
Trevor Bekolay Trevor Bekolay
Tyler Goodlet Tyler Goodlet
Vasily Kuznetsov Vasily Kuznetsov
Victor Uriarte
Vlad Dragos Vlad Dragos
Wouter van Ackooy Wouter van Ackooy
Xuecong Liao Xuecong Liao

View File

@ -1,18 +1,32 @@
3.1.0.dev 3.1.0.dev (unreleased)
========= ======================
New Features New Features
------------ ------------
* * Added an ini option ``doctest_encoding`` to specify which encoding to use for doctest files.
Thanks `@wheerd`_ for the PR (`#2101`_).
* * pytest now warns when a callable ids raises in a parametrized test. Thanks `@fogo`_ for the PR.
* ``pytest.warns`` now checks for subclass relationship rather than
class equality. Thanks `@lesteve`_ for the PR (`#2166`_)
* ``pytest.raises`` now asserts that the error message matches a text or regex
with the ``match`` keyword argument. Thanks `@Kriechi`_ for the PR.
Changes Changes
------- -------
* Old-style classes have been changed to new-style classes in order to improve
compatibility with Python 2. Thanks to `@MichalTHEDUDE`_ and `@mandeep`_ for the PR (`#2147`_).
* It is now possible to skip test classes from being collected by setting a
``__test__`` attribute to ``False`` in the class body (`#2007`_). Thanks
to `@syre`_ for the report and `@lwm`_ for the PR.
* Testcase reports with a ``url`` attribute will now properly write this to junitxml. * Testcase reports with a ``url`` attribute will now properly write this to junitxml.
Thanks `@fushi`_ for the PR (`#1874`_). Thanks `@fushi`_ for the PR (`#1874`_).
@ -21,30 +35,48 @@ Changes
assertion messages if verbosity < 2 (`#1512`_). assertion messages if verbosity < 2 (`#1512`_).
Thanks `@mattduck`_ for the PR Thanks `@mattduck`_ for the PR
* ``--pdbcls`` no longer implies ``--pdb``. This makes it possible to use * ``--pdbcls`` no longer implies ``--pdb``. This makes it possible to use
``addopts=--pdbcls=module.SomeClass`` on ``pytest.ini``. Thanks `@davidszotten`_ for ``addopts=--pdbcls=module.SomeClass`` on ``pytest.ini``. Thanks `@davidszotten`_ for
the PR (`#1952`_). the PR (`#1952`_).
* Change exception raised by ``capture.DontReadFromInput.fileno()`` from ``ValueError`` * Change exception raised by ``capture.DontReadFromInput.fileno()`` from ``ValueError``
to ``io.UnsupportedOperation``. Thanks `@vlad-dragos`_ for the PR. to ``io.UnsupportedOperation``. Thanks `@vlad-dragos`_ for the PR.
* fix `#2013`_: turn RecordedWarning into namedtupe, * fix `#2013`_: turn RecordedWarning into namedtupe,
to give it a comprehensible repr while preventing unwarranted modification to give it a comprehensible repr while preventing unwarranted modification
* fix `#2208`_: ensure a iteration limit for _pytest.compat.get_real_func.
Thanks `@RonnyPfannschmidt`_ for the Report and PR
* Modify ``pytest_make_parametrize_id()`` hook to accept ``argname`` as an
additional parameter.
Thanks `@unsignedint`_ for the PR.
* Add ``venv`` to the default ``norecursedirs`` setting.
Thanks `@The-Compiler`_ for the PR.
.. _@davidszotten: https://github.com/davidszotten .. _@davidszotten: https://github.com/davidszotten
.. _@fushi: https://github.com/fushi .. _@fushi: https://github.com/fushi
.. _@mattduck: https://github.com/mattduck .. _@mattduck: https://github.com/mattduck
.. _@wheerd: https://github.com/wheerd
.. _@fogo: https://github.com/fogo
.. _@mandeep: https://github.com/mandeep
.. _@MichalTHEDUDE: https://github.com/MichalTHEDUDE
.. _@unsignedint: https://github.com/unsignedint
.. _@Kriechi: https://github.com/Kriechi
.. _#1512: https://github.com/pytest-dev/pytest/issues/1512 .. _#1512: https://github.com/pytest-dev/pytest/issues/1512
.. _#1874: https://github.com/pytest-dev/pytest/pull/1874 .. _#1874: https://github.com/pytest-dev/pytest/pull/1874
.. _#1952: https://github.com/pytest-dev/pytest/pull/1952 .. _#1952: https://github.com/pytest-dev/pytest/pull/1952
.. _#2007: https://github.com/pytest-dev/pytest/issues/2007
.. _#2013: https://github.com/pytest-dev/pytest/issues/2013 .. _#2013: https://github.com/pytest-dev/pytest/issues/2013
.. _#2101: https://github.com/pytest-dev/pytest/pull/2101
.. _#2166: https://github.com/pytest-dev/pytest/pull/2166
.. _#2147: https://github.com/pytest-dev/pytest/issues/2147
.. _#2208: https://github.com/pytest-dev/pytest/issues/2208
3.0.7 (unreleased)
3.0.5.dev0 =======================
==========
* *
@ -54,11 +86,125 @@ Changes
* *
*
3.0.6 (2017-01-22)
==================
* pytest no longer generates ``PendingDeprecationWarning`` from its own operations, which was introduced by mistake in version ``3.0.5`` (`#2118`_).
Thanks to `@nicoddemus`_ for the report and `@RonnyPfannschmidt`_ for the PR.
3.0.4 * pytest no longer recognizes coroutine functions as yield tests (`#2129`_).
===== Thanks to `@malinoff`_ for the PR.
* Plugins loaded by the ``PYTEST_PLUGINS`` environment variable are now automatically
considered for assertion rewriting (`#2185`_).
Thanks `@nicoddemus`_ for the PR.
* Improve error message when pytest.warns fails (`#2150`_). The type(s) of the
expected warnings and the list of caught warnings is added to the
error message. Thanks `@lesteve`_ for the PR.
* Fix ``pytester`` internal plugin to work correctly with latest versions of
``zope.interface`` (`#1989`_). Thanks `@nicoddemus`_ for the PR.
* Assert statements of the ``pytester`` plugin again benefit from assertion rewriting (`#1920`_).
Thanks `@RonnyPfannschmidt`_ for the report and `@nicoddemus`_ for the PR.
* Specifying tests with colons like ``test_foo.py::test_bar`` for tests in
subdirectories with ini configuration files now uses the correct ini file
(`#2148`_). Thanks `@pelme`_.
* Fail ``testdir.runpytest().assert_outcomes()`` explicitly if the pytest
terminal output it relies on is missing. Thanks to `@eli-b`_ for the PR.
.. _@lesteve: https://github.com/lesteve
.. _@malinoff: https://github.com/malinoff
.. _@pelme: https://github.com/pelme
.. _@eli-b: https://github.com/eli-b
.. _#2118: https://github.com/pytest-dev/pytest/issues/2118
.. _#1989: https://github.com/pytest-dev/pytest/issues/1989
.. _#1920: https://github.com/pytest-dev/pytest/issues/1920
.. _#2129: https://github.com/pytest-dev/pytest/issues/2129
.. _#2148: https://github.com/pytest-dev/pytest/issues/2148
.. _#2150: https://github.com/pytest-dev/pytest/issues/2150
.. _#2185: https://github.com/pytest-dev/pytest/issues/2185
3.0.5 (2016-12-05)
==================
* Add warning when not passing ``option=value`` correctly to ``-o/--override-ini`` (`#2105`_).
Also improved the help documentation. Thanks to `@mbukatov`_ for the report and
`@lwm`_ for the PR.
* Now ``--confcutdir`` and ``--junit-xml`` are properly validated if they are directories
and filenames, respectively (`#2089`_ and `#2078`_). Thanks to `@lwm`_ for the PR.
* Add hint to error message hinting possible missing ``__init__.py`` (`#478`_). Thanks `@DuncanBetts`_.
* More accurately describe when fixture finalization occurs in documentation (`#687`_). Thanks `@DuncanBetts`_.
* Provide ``:ref:`` targets for ``recwarn.rst`` so we can use intersphinx referencing.
Thanks to `@dupuy`_ for the report and `@lwm`_ for the PR.
* In Python 2, use a simple ``+-`` ASCII string in the string representation of ``pytest.approx`` (for example ``"4 +- 4.0e-06"``)
because it is brittle to handle that in different contexts and representations internally in pytest
which can result in bugs such as `#2111`_. In Python 3, the representation still uses ``±`` (for example ``4 ± 4.0e-06``).
Thanks `@kerrick-lyft`_ for the report and `@nicoddemus`_ for the PR.
* Using ``item.Function``, ``item.Module``, etc., is now issuing deprecation warnings, prefer
``pytest.Function``, ``pytest.Module``, etc., instead (`#2034`_).
Thanks `@nmundar`_ for the PR.
* Fix error message using ``approx`` with complex numbers (`#2082`_).
Thanks `@adler-j`_ for the report and `@nicoddemus`_ for the PR.
* Fixed false-positives warnings from assertion rewrite hook for modules imported more than
once by the ``pytest_plugins`` mechanism.
Thanks `@nicoddemus`_ for the PR.
* Remove an internal cache which could cause hooks from ``conftest.py`` files in
sub-directories to be called in other directories incorrectly (`#2016`_).
Thanks `@d-b-w`_ for the report and `@nicoddemus`_ for the PR.
* Remove internal code meant to support earlier Python 3 versions that produced the side effect
of leaving ``None`` in ``sys.modules`` when expressions were evaluated by pytest (for example passing a condition
as a string to ``pytest.mark.skipif``)(`#2103`_).
Thanks `@jaraco`_ for the report and `@nicoddemus`_ for the PR.
* Cope gracefully with a .pyc file with no matching .py file (`#2038`_). Thanks
`@nedbat`_.
.. _@syre: https://github.com/syre
.. _@adler-j: https://github.com/adler-j
.. _@d-b-w: https://bitbucket.org/d-b-w/
.. _@DuncanBetts: https://github.com/DuncanBetts
.. _@dupuy: https://bitbucket.org/dupuy/
.. _@kerrick-lyft: https://github.com/kerrick-lyft
.. _@lwm: https://github.com/lwm
.. _@mbukatov: https://github.com/mbukatov
.. _@nedbat: https://github.com/nedbat
.. _@nmundar: https://github.com/nmundar
.. _#2016: https://github.com/pytest-dev/pytest/issues/2016
.. _#2034: https://github.com/pytest-dev/pytest/issues/2034
.. _#2038: https://github.com/pytest-dev/pytest/issues/2038
.. _#2078: https://github.com/pytest-dev/pytest/issues/2078
.. _#2082: https://github.com/pytest-dev/pytest/issues/2082
.. _#2089: https://github.com/pytest-dev/pytest/issues/2089
.. _#2103: https://github.com/pytest-dev/pytest/issues/2103
.. _#2105: https://github.com/pytest-dev/pytest/issues/2105
.. _#2111: https://github.com/pytest-dev/pytest/issues/2111
.. _#478: https://github.com/pytest-dev/pytest/issues/478
.. _#687: https://github.com/pytest-dev/pytest/issues/687
3.0.4 (2016-11-09)
==================
* Import errors when collecting test modules now display the full traceback (`#1976`_). * Import errors when collecting test modules now display the full traceback (`#1976`_).
Thanks `@cwitty`_ for the report and `@nicoddemus`_ for the PR. Thanks `@cwitty`_ for the report and `@nicoddemus`_ for the PR.
@ -111,8 +257,8 @@ Changes
.. _#1649: https://github.com/pytest-dev/pytest/issues/1649 .. _#1649: https://github.com/pytest-dev/pytest/issues/1649
3.0.3 3.0.3 (2016-09-28)
===== ==================
* The ``ids`` argument to ``parametrize`` again accepts ``unicode`` strings * The ``ids`` argument to ``parametrize`` again accepts ``unicode`` strings
in Python 2 (`#1905`_). in Python 2 (`#1905`_).
@ -151,8 +297,8 @@ Changes
3.0.2 3.0.2 (2016-09-01)
===== ==================
* Improve error message when passing non-string ids to ``pytest.mark.parametrize`` (`#1857`_). * Improve error message when passing non-string ids to ``pytest.mark.parametrize`` (`#1857`_).
Thanks `@okken`_ for the report and `@nicoddemus`_ for the PR. Thanks `@okken`_ for the report and `@nicoddemus`_ for the PR.
@ -191,19 +337,8 @@ Changes
.. _#1898: https://github.com/pytest-dev/pytest/issues/1898 .. _#1898: https://github.com/pytest-dev/pytest/issues/1898
3.0.2.dev 3.0.1 (2016-08-23)
========= ==================
*
*
*
*
3.0.1
=====
* Fix regression when ``importorskip`` is used at module level (`#1822`_). * Fix regression when ``importorskip`` is used at module level (`#1822`_).
Thanks `@jaraco`_ and `@The-Compiler`_ for the report and `@nicoddemus`_ for the PR. Thanks `@jaraco`_ and `@The-Compiler`_ for the report and `@nicoddemus`_ for the PR.
@ -228,8 +363,8 @@ Changes
.. _#1849: https://github.com/pytest-dev/pytest/issues/1849 .. _#1849: https://github.com/pytest-dev/pytest/issues/1849
3.0.0 3.0.0 (2016-08-18)
===== ==================
**Incompatible changes** **Incompatible changes**
@ -654,8 +789,8 @@ time or change existing behaviors in order to make them less surprising/more use
.. _@matthiasha: https://github.com/matthiasha .. _@matthiasha: https://github.com/matthiasha
2.9.2 2.9.2 (2016-05-31)
===== ==================
**Bug Fixes** **Bug Fixes**
@ -666,7 +801,7 @@ time or change existing behaviors in order to make them less surprising/more use
Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_ Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_
for PR the (`#1524`_). for PR the (`#1524`_).
* Fix win32 path issue when puttinging custom config file with absolute path * Fix win32 path issue when putting custom config file with absolute path
in ``pytest.main("-c your_absolute_path")``. in ``pytest.main("-c your_absolute_path")``.
* Fix maximum recursion depth detection when raised error class is not aware * Fix maximum recursion depth detection when raised error class is not aware
@ -693,8 +828,8 @@ time or change existing behaviors in order to make them less surprising/more use
.. _@astraw38: https://github.com/astraw38 .. _@astraw38: https://github.com/astraw38
2.9.1 2.9.1 (2016-03-17)
===== ==================
**Bug Fixes** **Bug Fixes**
@ -729,8 +864,8 @@ time or change existing behaviors in order to make them less surprising/more use
.. _@asottile: https://github.com/asottile .. _@asottile: https://github.com/asottile
2.9.0 2.9.0 (2016-02-29)
===== ==================
**New Features** **New Features**
@ -850,13 +985,13 @@ time or change existing behaviors in order to make them less surprising/more use
.. _@pquentin: https://github.com/pquentin .. _@pquentin: https://github.com/pquentin
.. _@ioggstream: https://github.com/ioggstream .. _@ioggstream: https://github.com/ioggstream
2.8.7 2.8.7 (2016-01-24)
===== ==================
- fix #1338: use predictable object resolution for monkeypatch - fix #1338: use predictable object resolution for monkeypatch
2.8.6 2.8.6 (2016-01-21)
===== ==================
- fix #1259: allow for double nodeids in junitxml, - fix #1259: allow for double nodeids in junitxml,
this was a regression failing plugins combinations this was a regression failing plugins combinations
@ -887,8 +1022,8 @@ time or change existing behaviors in order to make them less surprising/more use
Thanks Georgy Dyuldin for the PR. Thanks Georgy Dyuldin for the PR.
2.8.5 2.8.5 (2015-12-11)
===== ==================
- fix #1243: fixed issue where class attributes injected during collection could break pytest. - fix #1243: fixed issue where class attributes injected during collection could break pytest.
PR by Alexei Kozlenok, thanks Ronny Pfannschmidt and Bruno Oliveira for the review and help. PR by Alexei Kozlenok, thanks Ronny Pfannschmidt and Bruno Oliveira for the review and help.
@ -901,8 +1036,8 @@ time or change existing behaviors in order to make them less surprising/more use
Bruno Oliveira for the PR. Bruno Oliveira for the PR.
2.8.4 2.8.4 (2015-12-06)
===== ==================
- fix #1190: ``deprecated_call()`` now works when the deprecated - fix #1190: ``deprecated_call()`` now works when the deprecated
function has been already called by another test in the same function has been already called by another test in the same
@ -925,8 +1060,8 @@ time or change existing behaviors in order to make them less surprising/more use
- a number of documentation modernizations wrt good practices. - a number of documentation modernizations wrt good practices.
Thanks Bruno Oliveira for the PR. Thanks Bruno Oliveira for the PR.
2.8.3 2.8.3 (2015-11-18)
===== ==================
- fix #1169: add __name__ attribute to testcases in TestCaseFunction to - fix #1169: add __name__ attribute to testcases in TestCaseFunction to
support the @unittest.skip decorator on functions and methods. support the @unittest.skip decorator on functions and methods.
@ -953,8 +1088,8 @@ time or change existing behaviors in order to make them less surprising/more use
system integrity protection (thanks Florian) system integrity protection (thanks Florian)
2.8.2 2.8.2 (2015-10-07)
===== ==================
- fix #1085: proper handling of encoding errors when passing encoded byte - fix #1085: proper handling of encoding errors when passing encoded byte
strings to pytest.parametrize in Python 2. strings to pytest.parametrize in Python 2.
@ -973,8 +1108,8 @@ time or change existing behaviors in order to make them less surprising/more use
Thanks Sergey B Kirpichev and Vital Kudzelka for contributing and Bruno Thanks Sergey B Kirpichev and Vital Kudzelka for contributing and Bruno
Oliveira for the PR. Oliveira for the PR.
2.8.1 2.8.1 (2015-09-29)
===== ==================
- fix #1034: Add missing nodeid on pytest_logwarning call in - fix #1034: Add missing nodeid on pytest_logwarning call in
addhook. Thanks Simon Gomizelj for the PR. addhook. Thanks Simon Gomizelj for the PR.
@ -998,7 +1133,7 @@ time or change existing behaviors in order to make them less surprising/more use
- (experimental) adapt more SEMVER style versioning and change meaning of - (experimental) adapt more SEMVER style versioning and change meaning of
master branch in git repo: "master" branch now keeps the bugfixes, changes master branch in git repo: "master" branch now keeps the bugfixes, changes
aimed for micro releases. "features" branch will only be be released aimed for micro releases. "features" branch will only be released
with minor or major pytest releases. with minor or major pytest releases.
- Fix issue #766 by removing documentation references to distutils. - Fix issue #766 by removing documentation references to distutils.
@ -1020,8 +1155,8 @@ time or change existing behaviors in order to make them less surprising/more use
- fix issue 1029: transform errors when writing cache values into pytest-warnings - fix issue 1029: transform errors when writing cache values into pytest-warnings
2.8.0 2.8.0 (2015-09-18)
===== ==================
- new ``--lf`` and ``-ff`` options to run only the last failing tests or - new ``--lf`` and ``-ff`` options to run only the last failing tests or
"failing tests first" from the last run. This functionality is provided "failing tests first" from the last run. This functionality is provided
@ -1132,7 +1267,7 @@ time or change existing behaviors in order to make them less surprising/more use
- new option ``--import-mode`` to allow to change test module importing - new option ``--import-mode`` to allow to change test module importing
behaviour to append to sys.path instead of prepending. This better allows behaviour to append to sys.path instead of prepending. This better allows
to run test modules against installated versions of a package even if the to run test modules against installed versions of a package even if the
package under test has the same import root. In this example:: package under test has the same import root. In this example::
testing/__init__.py testing/__init__.py
@ -1210,8 +1345,8 @@ time or change existing behaviors in order to make them less surprising/more use
properly used to discover ``rootdir`` and ``ini`` files. properly used to discover ``rootdir`` and ``ini`` files.
Thanks Peter Lauri for the report and Bruno Oliveira for the PR. Thanks Peter Lauri for the report and Bruno Oliveira for the PR.
2.7.3 (compared to 2.7.2) 2.7.3 (2015-09-15)
============================= ==================
- Allow 'dev', 'rc', or other non-integer version strings in ``importorskip``. - Allow 'dev', 'rc', or other non-integer version strings in ``importorskip``.
Thanks to Eric Hunsberger for the PR. Thanks to Eric Hunsberger for the PR.
@ -1253,8 +1388,8 @@ time or change existing behaviors in order to make them less surprising/more use
directories created by this fixture (defaults to $TEMP/pytest-$USER). directories created by this fixture (defaults to $TEMP/pytest-$USER).
Thanks Bruno Oliveira for the PR. Thanks Bruno Oliveira for the PR.
2.7.2 (compared to 2.7.1) 2.7.2 (2015-06-23)
============================= ==================
- fix issue767: pytest.raises value attribute does not contain the exception - fix issue767: pytest.raises value attribute does not contain the exception
instance on Python 2.6. Thanks Eric Siegerman for providing the test instance on Python 2.6. Thanks Eric Siegerman for providing the test
@ -1282,15 +1417,15 @@ time or change existing behaviors in order to make them less surprising/more use
which has a refined algorithm for traceback generation. which has a refined algorithm for traceback generation.
2.7.1 (compared to 2.7.0) 2.7.1 (2015-05-19)
============================= ==================
- fix issue731: do not get confused by the braces which may be present - fix issue731: do not get confused by the braces which may be present
and unbalanced in an object's repr while collapsing False and unbalanced in an object's repr while collapsing False
explanations. Thanks Carl Meyer for the report and test case. explanations. Thanks Carl Meyer for the report and test case.
- fix issue553: properly handling inspect.getsourcelines failures in - fix issue553: properly handling inspect.getsourcelines failures in
FixtureLookupError which would lead to to an internal error, FixtureLookupError which would lead to an internal error,
obfuscating the original problem. Thanks talljosh for initial obfuscating the original problem. Thanks talljosh for initial
diagnose/patch and Bruno Oliveira for final patch. diagnose/patch and Bruno Oliveira for final patch.
@ -1315,8 +1450,8 @@ time or change existing behaviors in order to make them less surprising/more use
- reintroduced _pytest fixture of the pytester plugin which is used - reintroduced _pytest fixture of the pytester plugin which is used
at least by pytest-xdist. at least by pytest-xdist.
2.7.0 (compared to 2.6.4) 2.7.0 (2015-03-26)
============================= ==================
- fix issue435: make reload() work when assert rewriting is active. - fix issue435: make reload() work when assert rewriting is active.
Thanks Daniel Hahler. Thanks Daniel Hahler.
@ -1385,8 +1520,8 @@ time or change existing behaviors in order to make them less surprising/more use
``sys.last_traceback`` are set, so that a user can inspect the error ``sys.last_traceback`` are set, so that a user can inspect the error
via postmortem debugging (almarklein). via postmortem debugging (almarklein).
2.6.4 2.6.4 (2014-10-24)
===== ==================
- Improve assertion failure reporting on iterables, by using ndiff and - Improve assertion failure reporting on iterables, by using ndiff and
pprint. pprint.
@ -1414,8 +1549,8 @@ time or change existing behaviors in order to make them less surprising/more use
- fix issue614: fixed pastebin support. - fix issue614: fixed pastebin support.
2.6.3 2.6.3 (2014-09-24)
===== ==================
- fix issue575: xunit-xml was reporting collection errors as failures - fix issue575: xunit-xml was reporting collection errors as failures
instead of errors, thanks Oleg Sinyavskiy. instead of errors, thanks Oleg Sinyavskiy.
@ -1433,7 +1568,7 @@ time or change existing behaviors in order to make them less surprising/more use
- fix conftest related fixture visibility issue: when running with a - fix conftest related fixture visibility issue: when running with a
CWD outside of a test package pytest would get fixture discovery wrong. CWD outside of a test package pytest would get fixture discovery wrong.
Thanks to Wolfgang Schnerring for figuring out a reproducable example. Thanks to Wolfgang Schnerring for figuring out a reproducible example.
- Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the - Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the
timeout when interactively entering pdb). Thanks Wolfgang Schnerring. timeout when interactively entering pdb). Thanks Wolfgang Schnerring.
@ -1441,8 +1576,8 @@ time or change existing behaviors in order to make them less surprising/more use
- check xfail/skip also with non-python function test items. Thanks - check xfail/skip also with non-python function test items. Thanks
Floris Bruynooghe. Floris Bruynooghe.
2.6.2 2.6.2 (2014-09-05)
===== ==================
- Added function pytest.freeze_includes(), which makes it easy to embed - Added function pytest.freeze_includes(), which makes it easy to embed
pytest into executables using tools like cx_freeze. pytest into executables using tools like cx_freeze.
@ -1470,8 +1605,8 @@ time or change existing behaviors in order to make them less surprising/more use
replace the py.test introspection message but are shown in addition replace the py.test introspection message but are shown in addition
to them. to them.
2.6.1 2.6.1 (2014-08-07)
===== ==================
- No longer show line numbers in the --verbose output, the output is now - No longer show line numbers in the --verbose output, the output is now
purely the nodeid. The line number is still shown in failure reports. purely the nodeid. The line number is still shown in failure reports.
@ -1607,8 +1742,8 @@ time or change existing behaviors in order to make them less surprising/more use
in monkeypatch plugin. Improves output in documentation. in monkeypatch plugin. Improves output in documentation.
2.5.2 2.5.2 (2014-01-29)
===== ==================
- fix issue409 -- better interoperate with cx_freeze by not - fix issue409 -- better interoperate with cx_freeze by not
trying to import from collections.abc which causes problems trying to import from collections.abc which causes problems
@ -1632,11 +1767,11 @@ time or change existing behaviors in order to make them less surprising/more use
- fix issue429: comparing byte strings with non-ascii chars in assert - fix issue429: comparing byte strings with non-ascii chars in assert
expressions now work better. Thanks Floris Bruynooghe. expressions now work better. Thanks Floris Bruynooghe.
- make capfd/capsys.capture private, its unused and shouldnt be exposed - make capfd/capsys.capture private, its unused and shouldn't be exposed
2.5.1 2.5.1 (2013-12-17)
===== ==================
- merge new documentation styling PR from Tobias Bieniek. - merge new documentation styling PR from Tobias Bieniek.
@ -1656,8 +1791,8 @@ time or change existing behaviors in order to make them less surprising/more use
2.5.0 2.5.0 (2013-12-12)
===== ==================
- dropped python2.5 from automated release testing of pytest itself - dropped python2.5 from automated release testing of pytest itself
which means it's probably going to break soon (but still works which means it's probably going to break soon (but still works
@ -1689,7 +1824,7 @@ time or change existing behaviors in order to make them less surprising/more use
to problems for more than >966 non-function scoped parameters). to problems for more than >966 non-function scoped parameters).
- fix issue290 - there is preliminary support now for parametrizing - fix issue290 - there is preliminary support now for parametrizing
with repeated same values (sometimes useful to to test if calling with repeated same values (sometimes useful to test if calling
a second time works as with the first time). a second time works as with the first time).
- close issue240 - document precisely how pytest module importing - close issue240 - document precisely how pytest module importing
@ -1792,8 +1927,8 @@ time or change existing behaviors in order to make them less surprising/more use
- fix verbose reporting for @mock'd test functions - fix verbose reporting for @mock'd test functions
2.4.2 2.4.2 (2013-10-04)
===== ==================
- on Windows require colorama and a newer py lib so that py.io.TerminalWriter() - on Windows require colorama and a newer py lib so that py.io.TerminalWriter()
now uses colorama instead of its own ctypes hacks. (fixes issue365) now uses colorama instead of its own ctypes hacks. (fixes issue365)
@ -1823,8 +1958,8 @@ time or change existing behaviors in order to make them less surprising/more use
- add pluginmanager.do_configure(config) as a link to - add pluginmanager.do_configure(config) as a link to
config.do_configure() for plugin-compatibility config.do_configure() for plugin-compatibility
2.4.1 2.4.1 (2013-10-02)
===== ==================
- When using parser.addoption() unicode arguments to the - When using parser.addoption() unicode arguments to the
"type" keyword should also be converted to the respective types. "type" keyword should also be converted to the respective types.
@ -1903,7 +2038,7 @@ new features:
- fix issue322: tearDownClass is not run if setUpClass failed. Thanks - fix issue322: tearDownClass is not run if setUpClass failed. Thanks
Mathieu Agopian for the initial fix. Also make all of pytest/nose Mathieu Agopian for the initial fix. Also make all of pytest/nose
finalizer mimick the same generic behaviour: if a setupX exists and finalizer mimic the same generic behaviour: if a setupX exists and
fails, don't run teardownX. This internally introduces a new method fails, don't run teardownX. This internally introduces a new method
"node.addfinalizer()" helper which can only be called during the setup "node.addfinalizer()" helper which can only be called during the setup
phase of a node. phase of a node.
@ -2008,8 +2143,8 @@ Bug fixes:
".section(title)" and ".line(msg)" methods to print extra ".section(title)" and ".line(msg)" methods to print extra
information at the end of a test run. information at the end of a test run.
2.3.5 2.3.5 (2013-04-30)
===== ==================
- fix issue169: respect --tb=style with setup/teardown errors as well. - fix issue169: respect --tb=style with setup/teardown errors as well.
@ -2022,11 +2157,11 @@ Bug fixes:
(thanks Adam Goucher) (thanks Adam Goucher)
- Issue 265 - integrate nose setup/teardown with setupstate - Issue 265 - integrate nose setup/teardown with setupstate
so it doesnt try to teardown if it did not setup so it doesn't try to teardown if it did not setup
- issue 271 - dont write junitxml on slave nodes - issue 271 - don't write junitxml on slave nodes
- Issue 274 - dont try to show full doctest example - Issue 274 - don't try to show full doctest example
when doctest does not know the example location when doctest does not know the example location
- issue 280 - disable assertion rewriting on buggy CPython 2.6.0 - issue 280 - disable assertion rewriting on buggy CPython 2.6.0
@ -2062,7 +2197,7 @@ Bug fixes:
- allow to specify prefixes starting with "_" when - allow to specify prefixes starting with "_" when
customizing python_functions test discovery. (thanks Graham Horler) customizing python_functions test discovery. (thanks Graham Horler)
- improve PYTEST_DEBUG tracing output by puting - improve PYTEST_DEBUG tracing output by putting
extra data on a new lines with additional indent extra data on a new lines with additional indent
- ensure OutcomeExceptions like skip/fail have initialized exception attributes - ensure OutcomeExceptions like skip/fail have initialized exception attributes
@ -2073,8 +2208,8 @@ Bug fixes:
- fix issue266 - accept unicode in MarkEvaluator expressions - fix issue266 - accept unicode in MarkEvaluator expressions
2.3.4 2.3.4 (2012-11-20)
===== ==================
- yielded test functions will now have autouse-fixtures active but - yielded test functions will now have autouse-fixtures active but
cannot accept fixtures as funcargs - it's anyway recommended to cannot accept fixtures as funcargs - it's anyway recommended to
@ -2093,8 +2228,8 @@ Bug fixes:
need to write as -k "TestClass and test_method" to match a certain need to write as -k "TestClass and test_method" to match a certain
method in a certain test class. method in a certain test class.
2.3.3 2.3.3 (2012-11-06)
===== ==================
- fix issue214 - parse modules that contain special objects like e. g. - fix issue214 - parse modules that contain special objects like e. g.
flask's request object which blows up on getattr access if no request flask's request object which blows up on getattr access if no request
@ -2111,7 +2246,7 @@ Bug fixes:
- fix issue209 - reintroduce python2.4 support by depending on newer - fix issue209 - reintroduce python2.4 support by depending on newer
pylib which re-introduced statement-finding for pre-AST interpreters pylib which re-introduced statement-finding for pre-AST interpreters
- nose support: only call setup if its a callable, thanks Andrew - nose support: only call setup if it's a callable, thanks Andrew
Taumoefolau Taumoefolau
- fix issue219 - add py2.4-3.3 classifiers to TROVE list - fix issue219 - add py2.4-3.3 classifiers to TROVE list
@ -2125,8 +2260,8 @@ Bug fixes:
- fix issue127 - improve documentation for pytest_addoption() and - fix issue127 - improve documentation for pytest_addoption() and
add a ``config.getoption(name)`` helper function for consistency. add a ``config.getoption(name)`` helper function for consistency.
2.3.2 2.3.2 (2012-10-25)
===== ==================
- fix issue208 and fix issue29 use new py version to avoid long pauses - fix issue208 and fix issue29 use new py version to avoid long pauses
when printing tracebacks in long modules when printing tracebacks in long modules
@ -2158,8 +2293,8 @@ Bug fixes:
- add tox.ini to pytest distribution so that ignore-dirs and others config - add tox.ini to pytest distribution so that ignore-dirs and others config
bits are properly distributed for maintainers who run pytest-own tests bits are properly distributed for maintainers who run pytest-own tests
2.3.1 2.3.1 (2012-10-20)
===== ==================
- fix issue202 - fix regression: using "self" from fixture functions now - fix issue202 - fix regression: using "self" from fixture functions now
works as expected (it's the same "self" instance that a test method works as expected (it's the same "self" instance that a test method
@ -2171,8 +2306,8 @@ Bug fixes:
- link to web pages from --markers output which provides help for - link to web pages from --markers output which provides help for
pytest.mark.* usage. pytest.mark.* usage.
2.3.0 2.3.0 (2012-10-19)
===== ==================
- fix issue202 - better automatic names for parametrized test functions - fix issue202 - better automatic names for parametrized test functions
- fix issue139 - introduce @pytest.fixture which allows direct scoping - fix issue139 - introduce @pytest.fixture which allows direct scoping
@ -2207,7 +2342,7 @@ Bug fixes:
- fix issue128: show captured output when capsys/capfd are used - fix issue128: show captured output when capsys/capfd are used
- fix issue179: propperly show the dependency chain of factories - fix issue179: properly show the dependency chain of factories
- pluginmanager.register(...) now raises ValueError if the - pluginmanager.register(...) now raises ValueError if the
plugin has been already registered or the name is taken plugin has been already registered or the name is taken
@ -2248,10 +2383,10 @@ Bug fixes:
- don't show deselected reason line if there is none - don't show deselected reason line if there is none
- py.test -vv will show all of assert comparisations instead of truncating - py.test -vv will show all of assert comparisons instead of truncating
2.2.4 2.2.4 (2012-05-22)
===== ==================
- fix error message for rewritten assertions involving the % operator - fix error message for rewritten assertions involving the % operator
- fix issue 126: correctly match all invalid xml characters for junitxml - fix issue 126: correctly match all invalid xml characters for junitxml
@ -2259,7 +2394,7 @@ Bug fixes:
- fix issue with unittest: now @unittest.expectedFailure markers should - fix issue with unittest: now @unittest.expectedFailure markers should
be processed correctly (you can also use @pytest.mark markers) be processed correctly (you can also use @pytest.mark markers)
- document integration with the extended distribute/setuptools test commands - document integration with the extended distribute/setuptools test commands
- fix issue 140: propperly get the real functions - fix issue 140: properly get the real functions
of bound classmethods for setup/teardown_class of bound classmethods for setup/teardown_class
- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net - fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net
- fix issue #143: call unconfigure/sessionfinish always when - fix issue #143: call unconfigure/sessionfinish always when
@ -2267,13 +2402,13 @@ Bug fixes:
- fix issue #144: better mangle test ids to junitxml classnames - fix issue #144: better mangle test ids to junitxml classnames
- upgrade distribute_setup.py to 0.6.27 - upgrade distribute_setup.py to 0.6.27
2.2.3 2.2.3 (2012-02-05)
===== ==================
- fix uploaded package to only include neccesary files - fix uploaded package to only include necessary files
2.2.2 2.2.2 (2012-02-05)
===== ==================
- fix issue101: wrong args to unittest.TestCase test function now - fix issue101: wrong args to unittest.TestCase test function now
produce better output produce better output
@ -2292,8 +2427,8 @@ Bug fixes:
- allow adding of attributes to test reports such that it also works - allow adding of attributes to test reports such that it also works
with distributed testing (no upgrade of pytest-xdist needed) with distributed testing (no upgrade of pytest-xdist needed)
2.2.1 2.2.1 (2011-12-16)
===== ==================
- fix issue99 (in pytest and py) internallerrors with resultlog now - fix issue99 (in pytest and py) internallerrors with resultlog now
produce better output - fixed by normalizing pytest_internalerror produce better output - fixed by normalizing pytest_internalerror
@ -2309,8 +2444,8 @@ Bug fixes:
- fix collection crash due to unknown-source collected items, thanks - fix collection crash due to unknown-source collected items, thanks
to Ralf Schmitt (fixed by depending on a more recent pylib) to Ralf Schmitt (fixed by depending on a more recent pylib)
2.2.0 2.2.0 (2011-11-18)
===== ==================
- fix issue90: introduce eager tearing down of test items so that - fix issue90: introduce eager tearing down of test items so that
teardown function are called earlier. teardown function are called earlier.
@ -2344,8 +2479,8 @@ Bug fixes:
- simplify junitxml output code by relying on py.xml - simplify junitxml output code by relying on py.xml
- add support for skip properties on unittest classes and functions - add support for skip properties on unittest classes and functions
2.1.3 2.1.3 (2011-10-18)
===== ==================
- fix issue79: assertion rewriting failed on some comparisons in boolops - fix issue79: assertion rewriting failed on some comparisons in boolops
- correctly handle zero length arguments (a la pytest '') - correctly handle zero length arguments (a la pytest '')
@ -2353,8 +2488,8 @@ Bug fixes:
- fix issue75 / skipping test failure on jython - fix issue75 / skipping test failure on jython
- fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests - fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests
2.1.2 2.1.2 (2011-09-24)
===== ==================
- fix assertion rewriting on files with windows newlines on some Python versions - fix assertion rewriting on files with windows newlines on some Python versions
- refine test discovery by package/module name (--pyargs), thanks Florian Mayer - refine test discovery by package/module name (--pyargs), thanks Florian Mayer
@ -2376,8 +2511,8 @@ Bug fixes:
- fix issue61: assertion rewriting on boolean operations with 3 or more operands - fix issue61: assertion rewriting on boolean operations with 3 or more operands
- you can now build a man page with "cd doc ; make man" - you can now build a man page with "cd doc ; make man"
2.1.0 2.1.0 (2011-07-09)
===== ==================
- fix issue53 call nosestyle setup functions with correct ordering - fix issue53 call nosestyle setup functions with correct ordering
- fix issue58 and issue59: new assertion code fixes - fix issue58 and issue59: new assertion code fixes
@ -2396,8 +2531,8 @@ Bug fixes:
- report KeyboardInterrupt even if interrupted during session startup - report KeyboardInterrupt even if interrupted during session startup
- fix issue 35 - provide PDF doc version and download link from index page - fix issue 35 - provide PDF doc version and download link from index page
2.0.3 2.0.3 (2011-05-11)
===== ==================
- fix issue38: nicer tracebacks on calls to hooks, particularly early - fix issue38: nicer tracebacks on calls to hooks, particularly early
configure/sessionstart ones configure/sessionstart ones
@ -2411,13 +2546,13 @@ Bug fixes:
- don't require zlib (and other libs) for genscript plugin without - don't require zlib (and other libs) for genscript plugin without
--genscript actually being used. --genscript actually being used.
- speed up skips (by not doing a full traceback represenation - speed up skips (by not doing a full traceback representation
internally) internally)
- fix issue37: avoid invalid characters in junitxml's output - fix issue37: avoid invalid characters in junitxml's output
2.0.2 2.0.2 (2011-03-09)
===== ==================
- tackle issue32 - speed up test runs of very quick test functions - tackle issue32 - speed up test runs of very quick test functions
by reducing the relative overhead by reducing the relative overhead
@ -2459,17 +2594,17 @@ Bug fixes:
this. this.
- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular - fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular
thanks to Laura Creighton who also revieved parts of the documentation. thanks to Laura Creighton who also reviewed parts of the documentation.
- fix slighly wrong output of verbose progress reporting for classes - fix slightly wrong output of verbose progress reporting for classes
(thanks Amaury) (thanks Amaury)
- more precise (avoiding of) deprecation warnings for node.Class|Function accesses - more precise (avoiding of) deprecation warnings for node.Class|Function accesses
- avoid std unittest assertion helper code in tracebacks (thanks Ronny) - avoid std unittest assertion helper code in tracebacks (thanks Ronny)
2.0.1 2.0.1 (2011-02-07)
===== ==================
- refine and unify initial capturing so that it works nicely - refine and unify initial capturing so that it works nicely
even if the logging module is used on an early-loaded conftest.py even if the logging module is used on an early-loaded conftest.py
@ -2517,12 +2652,12 @@ Bug fixes:
parametraization remains the "pytest_generate_tests" parametraization remains the "pytest_generate_tests"
mechanism, see the docs. mechanism, see the docs.
2.0.0 2.0.0 (2010-11-25)
===== ==================
- pytest-2.0 is now its own package and depends on pylib-2.0 - pytest-2.0 is now its own package and depends on pylib-2.0
- new ability: python -m pytest / python -m pytest.main ability - new ability: python -m pytest / python -m pytest.main ability
- new python invcation: pytest.main(args, plugins) to load - new python invocation: pytest.main(args, plugins) to load
some custom plugins early. some custom plugins early.
- try harder to run unittest test suites in a more compatible manner - try harder to run unittest test suites in a more compatible manner
by deferring setup/teardown semantics to the unittest package. by deferring setup/teardown semantics to the unittest package.
@ -2562,8 +2697,8 @@ Bug fixes:
- add ability to use "class" level for cached_setup helper - add ability to use "class" level for cached_setup helper
- fix strangeness: mark.* objects are now immutable, create new instances - fix strangeness: mark.* objects are now immutable, create new instances
1.3.4 1.3.4 (2010-09-14)
===== ==================
- fix issue111: improve install documentation for windows - fix issue111: improve install documentation for windows
- fix issue119: fix custom collectability of __init__.py as a module - fix issue119: fix custom collectability of __init__.py as a module
@ -2571,8 +2706,8 @@ Bug fixes:
- fix issue115: unify internal exception passthrough/catching/GeneratorExit - fix issue115: unify internal exception passthrough/catching/GeneratorExit
- fix issue118: new --tb=native for presenting cpython-standard exceptions - fix issue118: new --tb=native for presenting cpython-standard exceptions
1.3.3 1.3.3 (2010-07-30)
===== ==================
- fix issue113: assertion representation problem with triple-quoted strings - fix issue113: assertion representation problem with triple-quoted strings
(and possibly other cases) (and possibly other cases)
@ -2586,8 +2721,8 @@ Bug fixes:
(thanks Armin Ronacher for reporting) (thanks Armin Ronacher for reporting)
- remove trailing whitespace in all py/text distribution files - remove trailing whitespace in all py/text distribution files
1.3.2 1.3.2 (2010-07-08)
===== ==================
**New features** **New features**
@ -2659,8 +2794,8 @@ Bug fixes:
- fix homedir detection on Windows - fix homedir detection on Windows
- ship distribute_setup.py version 0.6.13 - ship distribute_setup.py version 0.6.13
1.3.1 1.3.1 (2010-05-25)
===== ==================
**New features** **New features**
@ -2729,8 +2864,8 @@ Bug fixes:
(and internally be more careful when presenting unexpected byte sequences) (and internally be more careful when presenting unexpected byte sequences)
1.3.0 1.3.0 (2010-05-05)
===== ==================
- deprecate --report option in favour of a new shorter and easier to - deprecate --report option in favour of a new shorter and easier to
remember -r option: it takes a string argument consisting of any remember -r option: it takes a string argument consisting of any
@ -2761,7 +2896,7 @@ Bug fixes:
- extend and refine xfail mechanism: - extend and refine xfail mechanism:
``@py.test.mark.xfail(run=False)`` do not run the decorated test ``@py.test.mark.xfail(run=False)`` do not run the decorated test
``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries ``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries
specifiying ``--runxfail`` on command line virtually ignores xfail markers specifying ``--runxfail`` on command line virtually ignores xfail markers
- expose (previously internal) commonly useful methods: - expose (previously internal) commonly useful methods:
py.io.get_terminal_with() -> return terminal width py.io.get_terminal_with() -> return terminal width
@ -2794,8 +2929,8 @@ Bug fixes:
- added links to the new capturelog and coverage plugins - added links to the new capturelog and coverage plugins
1.2.0 1.2.0 (2010-01-18)
===== ==================
- refined usage and options for "py.cleanup":: - refined usage and options for "py.cleanup"::
@ -2833,8 +2968,8 @@ Bug fixes:
- fix plugin links - fix plugin links
1.1.1 1.1.1 (2009-11-24)
===== ==================
- moved dist/looponfailing from py.test core into a new - moved dist/looponfailing from py.test core into a new
separately released pytest-xdist plugin. separately released pytest-xdist plugin.
@ -2917,8 +3052,8 @@ Bug fixes:
- fix docs, fix internal bin/ script generation - fix docs, fix internal bin/ script generation
1.1.0 1.1.0 (2009-11-05)
===== ==================
- introduce automatic plugin registration via 'pytest11' - introduce automatic plugin registration via 'pytest11'
entrypoints via setuptools' pkg_resources.iter_entry_points entrypoints via setuptools' pkg_resources.iter_entry_points
@ -2986,7 +3121,7 @@ Bug fixes:
* add the ability to specify a path for py.lookup to search in * add the ability to specify a path for py.lookup to search in
* fix a funcarg cached_setup bug probably only occuring * fix a funcarg cached_setup bug probably only occurring
in distributed testing and "module" scope with teardown. in distributed testing and "module" scope with teardown.
* many fixes and changes for making the code base python3 compatible, * many fixes and changes for making the code base python3 compatible,
@ -3021,16 +3156,16 @@ Bug fixes:
* simplified internal localpath implementation * simplified internal localpath implementation
1.0.2 1.0.2 (2009-08-27)
===== ==================
* fixing packaging issues, triggered by fedora redhat packaging, * fixing packaging issues, triggered by fedora redhat packaging,
also added doc, examples and contrib dirs to the tarball. also added doc, examples and contrib dirs to the tarball.
* added a documentation link to the new django plugin. * added a documentation link to the new django plugin.
1.0.1 1.0.1 (2009-08-19)
===== ==================
* added a 'pytest_nose' plugin which handles nose.SkipTest, * added a 'pytest_nose' plugin which handles nose.SkipTest,
nose-style function/method/generator setup/teardown and nose-style function/method/generator setup/teardown and
@ -3063,14 +3198,14 @@ Bug fixes:
* simplified multicall mechanism and plugin architecture, * simplified multicall mechanism and plugin architecture,
renamed some internal methods and argnames renamed some internal methods and argnames
1.0.0 1.0.0 (2009-08-04)
===== ==================
* more terse reporting try to show filesystem path relatively to current dir * more terse reporting try to show filesystem path relatively to current dir
* improve xfail output a bit * improve xfail output a bit
1.0.0b9 1.0.0b9 (2009-07-31)
======= ====================
* cleanly handle and report final teardown of test setup * cleanly handle and report final teardown of test setup
@ -3103,8 +3238,8 @@ Bug fixes:
* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr) * item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr)
1.0.0b8 1.0.0b8 (2009-07-22)
======= ====================
* pytest_unittest-plugin is now enabled by default * pytest_unittest-plugin is now enabled by default
@ -3157,8 +3292,8 @@ Bug fixes:
* make __name__ == "__channelexec__" for remote_exec code * make __name__ == "__channelexec__" for remote_exec code
1.0.0b3 1.0.0b3 (2009-06-19)
======= ====================
* plugin classes are removed: one now defines * plugin classes are removed: one now defines
hooks directly in conftest.py or global pytest_*.py hooks directly in conftest.py or global pytest_*.py
@ -3252,10 +3387,10 @@ serve as a reference for developers.
* fixed issue with 2.5 type representations in py.test [45483, 45484] * fixed issue with 2.5 type representations in py.test [45483, 45484]
* made that internal reporting issues displaying is done atomically in py.test * made that internal reporting issues displaying is done atomically in py.test
[45518] [45518]
* made that non-existing files are igored by the py.lookup script [45519] * made that non-existing files are ignored by the py.lookup script [45519]
* improved exception name creation in py.test [45535] * improved exception name creation in py.test [45535]
* made that less threads are used in execnet [merge in 45539] * made that less threads are used in execnet [merge in 45539]
* removed lock required for atomical reporting issue displaying in py.test * removed lock required for atomic reporting issue displaying in py.test
[45545] [45545]
* removed globals from execnet [45541, 45547] * removed globals from execnet [45541, 45547]
* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit * refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit

View File

@ -79,6 +79,16 @@ Pytest could always use more documentation. What exactly is needed?
You can also edit documentation files directly in the GitHub web interface, You can also edit documentation files directly in the GitHub web interface,
without using a local copy. This can be convenient for small fixes. without using a local copy. This can be convenient for small fixes.
.. note::
Build the documentation locally with the following command:
.. code:: bash
$ tox -e docs
The built documentation should be available in the ``doc/en/_build/``.
Where 'en' refers to the documentation language.
.. _submitplugin: .. _submitplugin:

View File

@ -24,31 +24,31 @@ An example of a simple test:
.. code-block:: python .. code-block:: python
# content of test_sample.py # content of test_sample.py
def func(x): def inc(x):
return x + 1 return x + 1
def test_answer(): def test_answer():
assert func(3) == 5 assert inc(3) == 5
To execute it:: To execute it::
$ pytest $ pytest
======= test session starts ======== ============================= test session starts =============================
collected 1 items collected 1 items
test_sample.py F test_sample.py F
======= FAILURES ======== ================================== FAILURES ===================================
_______ test_answer ________ _________________________________ test_answer _________________________________
def test_answer(): def test_answer():
> assert func(3) == 5 > assert inc(3) == 5
E assert 4 == 5 E assert 4 == 5
E + where 4 = func(3) E + where 4 = inc(3)
test_sample.py:5: AssertionError test_sample.py:5: AssertionError
======= 1 failed in 0.12 seconds ======== ========================== 1 failed in 0.04 seconds ===========================
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples. Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.

View File

@ -62,7 +62,7 @@ import sys
import os import os
from glob import glob from glob import glob
class FastFilesCompleter: class FastFilesCompleter(object):
'Fast file completer class' 'Fast file completer class'
def __init__(self, directories=True): def __init__(self, directories=True):
self.directories = directories self.directories = directories

View File

@ -4,7 +4,6 @@ from bisect import bisect_right
import sys import sys
import inspect, tokenize import inspect, tokenize
import py import py
from types import ModuleType
cpy_compile = compile cpy_compile = compile
try: try:
@ -192,14 +191,6 @@ class Source(object):
if flag & _AST_FLAG: if flag & _AST_FLAG:
return co return co
lines = [(x + "\n") for x in self.lines] lines = [(x + "\n") for x in self.lines]
if sys.version_info[0] >= 3:
# XXX py3's inspect.getsourcefile() checks for a module
# and a pep302 __loader__ ... we don't have a module
# at code compile-time so we need to fake it here
m = ModuleType("_pycodecompile_pseudo_module")
py.std.inspect.modulesbyfile[filename] = None
py.std.sys.modules[None] = m
m.__loader__ = 1
py.std.linecache.cache[filename] = (1, None, lines, filename) py.std.linecache.cache[filename] = (1, None, lines, filename)
return co return co

View File

@ -29,7 +29,7 @@ def pytest_namespace():
def register_assert_rewrite(*names): def register_assert_rewrite(*names):
"""Register a module name to be rewritten on import. """Register one or more module names to be rewritten on import.
This function will make sure that this module or all modules inside This function will make sure that this module or all modules inside
the package will get their assert statements rewritten. the package will get their assert statements rewritten.
@ -59,7 +59,7 @@ class DummyRewriteHook(object):
pass pass
class AssertionState: class AssertionState(object):
"""State for the assertion plugin.""" """State for the assertion plugin."""
def __init__(self, config, mode): def __init__(self, config, mode):

View File

@ -80,7 +80,12 @@ class AssertionRewritingHook(object):
tp = desc[2] tp = desc[2]
if tp == imp.PY_COMPILED: if tp == imp.PY_COMPILED:
if hasattr(imp, "source_from_cache"): if hasattr(imp, "source_from_cache"):
fn = imp.source_from_cache(fn) try:
fn = imp.source_from_cache(fn)
except ValueError:
# Python 3 doesn't like orphaned but still-importable
# .pyc files.
fn = fn[:-1]
else: else:
fn = fn[:-1] fn = fn[:-1]
elif tp != imp.PY_SOURCE: elif tp != imp.PY_SOURCE:
@ -181,16 +186,15 @@ class AssertionRewritingHook(object):
""" """
already_imported = set(names).intersection(set(sys.modules)) already_imported = set(names).intersection(set(sys.modules))
if already_imported: if already_imported:
for name in names: for name in already_imported:
if name not in self._rewritten_names: if name not in self._rewritten_names:
self._warn_already_imported(already_imported) self._warn_already_imported(name)
self._must_rewrite.update(names) self._must_rewrite.update(names)
def _warn_already_imported(self, names): def _warn_already_imported(self, name):
self.config.warn( self.config.warn(
'P1', 'P1',
'Modules are already imported so can not be re-written: %s' % 'Module already imported so can not be re-written: %s' % name)
','.join(names))
def load_module(self, name): def load_module(self, name):
# If there is an existing module object named 'fullname' in # If there is an existing module object named 'fullname' in

View File

@ -87,7 +87,7 @@ class Cache(object):
json.dump(value, f, indent=2, sort_keys=True) json.dump(value, f, indent=2, sort_keys=True)
class LFPlugin: class LFPlugin(object):
""" Plugin which implements the --lf (run last-failing) option """ """ Plugin which implements the --lf (run last-failing) option """
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config

View File

@ -57,7 +57,7 @@ def pytest_load_initial_conftests(early_config, parser, args):
sys.stderr.write(err) sys.stderr.write(err)
class CaptureManager: class CaptureManager(object):
def __init__(self, method): def __init__(self, method):
self._method = method self._method = method
@ -182,7 +182,7 @@ def capfd(request):
return c return c
class CaptureFixture: class CaptureFixture(object):
def __init__(self, captureclass, request): def __init__(self, captureclass, request):
self.captureclass = captureclass self.captureclass = captureclass
self.request = request self.request = request
@ -315,10 +315,10 @@ class MultiCapture(object):
return (self.out.snap() if self.out is not None else "", return (self.out.snap() if self.out is not None else "",
self.err.snap() if self.err is not None else "") self.err.snap() if self.err is not None else "")
class NoCapture: class NoCapture(object):
__init__ = start = done = suspend = resume = lambda *args: None __init__ = start = done = suspend = resume = lambda *args: None
class FDCapture: class FDCapture(object):
""" Capture IO to/from a given os-level filedescriptor. """ """ Capture IO to/from a given os-level filedescriptor. """
def __init__(self, targetfd, tmpfile=None): def __init__(self, targetfd, tmpfile=None):
@ -394,7 +394,7 @@ class FDCapture:
os.write(self.targetfd_save, data) os.write(self.targetfd_save, data)
class SysCapture: class SysCapture(object):
def __init__(self, fd, tmpfile=None): def __init__(self, fd, tmpfile=None):
name = patchsysdict[fd] name = patchsysdict[fd]
self._old = getattr(sys, name) self._old = getattr(sys, name)
@ -432,7 +432,7 @@ class SysCapture:
self._old.flush() self._old.flush()
class DontReadFromInput: class DontReadFromInput(object):
"""Temporary stub class. Ideally when stdin is accessed, the """Temporary stub class. Ideally when stdin is accessed, the
capturing should be turned off, with possibly all data captured capturing should be turned off, with possibly all data captured
so far sent to the screen. This should be configurable, though, so far sent to the screen. This should be configurable, though,

View File

@ -19,6 +19,7 @@ except ImportError: # pragma: no cover
# Only available in Python 3.4+ or as a backport # Only available in Python 3.4+ or as a backport
enum = None enum = None
_PY3 = sys.version_info > (3, 0) _PY3 = sys.version_info > (3, 0)
_PY2 = not _PY3 _PY2 = not _PY3
@ -26,6 +27,9 @@ _PY2 = not _PY3
NoneType = type(None) NoneType = type(None)
NOTSET = object() NOTSET = object()
PY36 = sys.version_info[:2] >= (3, 6)
MODULE_NOT_FOUND_ERROR = 'ModuleNotFoundError' if PY36 else 'ImportError'
if hasattr(inspect, 'signature'): if hasattr(inspect, 'signature'):
def _format_args(func): def _format_args(func):
return str(inspect.signature(func)) return str(inspect.signature(func))
@ -42,11 +46,18 @@ REGEX_TYPE = type(re.compile(''))
def is_generator(func): def is_generator(func):
try: genfunc = inspect.isgeneratorfunction(func)
return _pytest._code.getrawcode(func).co_flags & 32 # generator function return genfunc and not iscoroutinefunction(func)
except AttributeError: # builtin functions have no bytecode
# assume them to not be generators
return False def iscoroutinefunction(func):
"""Return True if func is a decorated coroutine function.
Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly,
which in turns also initializes the "logging" module as side-effect (see issue #8).
"""
return (getattr(func, '_is_coroutine', False) or
(hasattr(inspect, 'iscoroutinefunction') and inspect.iscoroutinefunction(func)))
def getlocation(function, curdir): def getlocation(function, curdir):
@ -169,8 +180,18 @@ def get_real_func(obj):
""" gets the real function object of the (possibly) wrapped object by """ gets the real function object of the (possibly) wrapped object by
functools.wraps or functools.partial. functools.wraps or functools.partial.
""" """
while hasattr(obj, "__wrapped__"): start_obj = obj
obj = obj.__wrapped__ for i in range(100):
new_obj = getattr(obj, '__wrapped__', None)
if new_obj is None:
break
obj = new_obj
else:
raise ValueError(
("could not find real function of {start}"
"\nstopped at {current}").format(
start=py.io.saferepr(start_obj),
current=py.io.saferepr(obj)))
if isinstance(obj, functools.partial): if isinstance(obj, functools.partial):
obj = obj.func obj = obj.func
return obj return obj
@ -200,7 +221,7 @@ def safe_getattr(object, name, default):
""" Like getattr but return default upon any Exception. """ Like getattr but return default upon any Exception.
Attribute access can potentially fail for 'evil' Python objects. Attribute access can potentially fail for 'evil' Python objects.
See issue214 See issue #214.
""" """
try: try:
return getattr(object, name, default) return getattr(object, name, default)

View File

@ -62,7 +62,7 @@ def main(args=None, plugins=None):
sys.stderr.write("ERROR: %s\n" %(msg,)) sys.stderr.write("ERROR: %s\n" %(msg,))
return 4 return 4
class cmdline: # compatibility namespace class cmdline(object): # compatibility namespace
main = staticmethod(main) main = staticmethod(main)
@ -70,6 +70,28 @@ class UsageError(Exception):
""" error in pytest usage or invocation""" """ error in pytest usage or invocation"""
def filename_arg(path, optname):
""" Argparse type validator for filename arguments.
:path: path of filename
:optname: name of the option
"""
if os.path.isdir(path):
raise UsageError("{0} must be a filename, given: {1}".format(optname, path))
return path
def directory_arg(path, optname):
"""Argparse type validator for directory arguments.
:path: path of directory
:optname: name of the option
"""
if not os.path.isdir(path):
raise UsageError("{0} must be a directory, given: {1}".format(optname, path))
return path
_preinit = [] _preinit = []
default_plugins = ( default_plugins = (
@ -380,31 +402,26 @@ class PytestPluginManager(PluginManager):
self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
def consider_module(self, mod): def consider_module(self, mod):
plugins = getattr(mod, 'pytest_plugins', []) self._import_plugin_specs(getattr(mod, 'pytest_plugins', []))
if isinstance(plugins, str):
plugins = [plugins]
self.rewrite_hook.mark_rewrite(*plugins)
self._import_plugin_specs(plugins)
def _import_plugin_specs(self, spec): def _import_plugin_specs(self, spec):
if spec: plugins = _get_plugin_specs_as_list(spec)
if isinstance(spec, str): for import_spec in plugins:
spec = spec.split(",") self.import_plugin(import_spec)
for import_spec in spec:
self.import_plugin(import_spec)
def import_plugin(self, modname): def import_plugin(self, modname):
# most often modname refers to builtin modules, e.g. "pytester", # most often modname refers to builtin modules, e.g. "pytester",
# "terminal" or "capture". Those plugins are registered under their # "terminal" or "capture". Those plugins are registered under their
# basename for historic purposes but must be imported with the # basename for historic purposes but must be imported with the
# _pytest prefix. # _pytest prefix.
assert isinstance(modname, str) assert isinstance(modname, str), "module name as string required, got %r" % modname
if self.get_plugin(modname) is not None: if self.get_plugin(modname) is not None:
return return
if modname in builtin_plugins: if modname in builtin_plugins:
importspec = "_pytest." + modname importspec = "_pytest." + modname
else: else:
importspec = modname importspec = modname
self.rewrite_hook.mark_rewrite(importspec)
try: try:
__import__(importspec) __import__(importspec)
except ImportError as e: except ImportError as e:
@ -425,7 +442,25 @@ class PytestPluginManager(PluginManager):
self.consider_module(mod) self.consider_module(mod)
class Parser: def _get_plugin_specs_as_list(specs):
"""
Parses a list of "plugin specs" and returns a list of plugin names.
Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in
which case it is returned as a list. Specs can also be `None` in which case an
empty list is returned.
"""
if specs is not None:
if isinstance(specs, str):
specs = specs.split(',') if specs else []
if not isinstance(specs, (list, tuple)):
raise UsageError("Plugin specs must be a ','-separated string or a "
"list/tuple of strings for plugin names. Given: %r" % specs)
return list(specs)
return []
class Parser(object):
""" Parser for command line arguments and ini-file values. """ Parser for command line arguments and ini-file values.
:ivar extra_info: dict of generic param -> value to display in case :ivar extra_info: dict of generic param -> value to display in case
@ -560,7 +595,7 @@ class ArgumentError(Exception):
return self.msg return self.msg
class Argument: class Argument(object):
"""class that mimics the necessary behaviour of optparse.Option """class that mimics the necessary behaviour of optparse.Option
its currently a least effort implementation its currently a least effort implementation
@ -690,7 +725,7 @@ class Argument:
return 'Argument({0})'.format(', '.join(args)) return 'Argument({0})'.format(', '.join(args))
class OptionGroup: class OptionGroup(object):
def __init__(self, name, description="", parser=None): def __init__(self, name, description="", parser=None):
self.name = name self.name = name
self.description = description self.description = description
@ -816,7 +851,7 @@ class CmdOptions(object):
def copy(self): def copy(self):
return CmdOptions(self.__dict__) return CmdOptions(self.__dict__)
class Notset: class Notset(object):
def __repr__(self): def __repr__(self):
return "<NOTSET>" return "<NOTSET>"
@ -1007,6 +1042,7 @@ class Config(object):
self.pluginmanager.load_setuptools_entrypoints(entrypoint_name) self.pluginmanager.load_setuptools_entrypoints(entrypoint_name)
self.pluginmanager.consider_env() self.pluginmanager.consider_env()
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy()) self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
confcutdir = self.known_args_namespace.confcutdir
if self.known_args_namespace.confcutdir is None and self.inifile: if self.known_args_namespace.confcutdir is None and self.inifile:
confcutdir = py.path.local(self.inifile).dirname confcutdir = py.path.local(self.inifile).dirname
self.known_args_namespace.confcutdir = confcutdir self.known_args_namespace.confcutdir = confcutdir
@ -1126,7 +1162,10 @@ class Config(object):
if self.getoption("override_ini", None): if self.getoption("override_ini", None):
for ini_config_list in self.option.override_ini: for ini_config_list in self.option.override_ini:
for ini_config in ini_config_list: for ini_config in ini_config_list:
(key, user_ini_value) = ini_config.split("=", 1) try:
(key, user_ini_value) = ini_config.split("=", 1)
except ValueError:
raise UsageError("-o/--override-ini expects option=value style.")
if key == name: if key == name:
value = user_ini_value value = user_ini_value
return value return value
@ -1202,25 +1241,20 @@ def getcfg(args, warnfunc=None):
return None, None, None return None, None, None
def get_common_ancestor(args): def get_common_ancestor(paths):
# args are what we get after early command line parsing (usually
# strings, but can be py.path.local objects as well)
common_ancestor = None common_ancestor = None
for arg in args: for path in paths:
if str(arg)[0] == "-": if not path.exists():
continue
p = py.path.local(arg)
if not p.exists():
continue continue
if common_ancestor is None: if common_ancestor is None:
common_ancestor = p common_ancestor = path
else: else:
if p.relto(common_ancestor) or p == common_ancestor: if path.relto(common_ancestor) or path == common_ancestor:
continue continue
elif common_ancestor.relto(p): elif common_ancestor.relto(path):
common_ancestor = p common_ancestor = path
else: else:
shared = p.common(common_ancestor) shared = path.common(common_ancestor)
if shared is not None: if shared is not None:
common_ancestor = shared common_ancestor = shared
if common_ancestor is None: if common_ancestor is None:
@ -1231,9 +1265,29 @@ def get_common_ancestor(args):
def get_dirs_from_args(args): def get_dirs_from_args(args):
return [d for d in (py.path.local(x) for x in args def is_option(x):
if not str(x).startswith("-")) return str(x).startswith('-')
if d.exists()]
def get_file_part_from_node_id(x):
return str(x).split('::')[0]
def get_dir_from_path(path):
if path.isdir():
return path
return py.path.local(path.dirname)
# These look like paths but may not exist
possible_paths = (
py.path.local(get_file_part_from_node_id(arg))
for arg in args
if not is_option(arg)
)
return [
get_dir_from_path(path)
for path in possible_paths
if path.exists()
]
def determine_setup(inifile, args, warnfunc=None): def determine_setup(inifile, args, warnfunc=None):

View File

@ -43,7 +43,7 @@ def pytest_configure(config):
pytestPDB._pdb_cls = pdb_cls pytestPDB._pdb_cls = pdb_cls
config._cleanup.append(fin) config._cleanup.append(fin)
class pytestPDB: class pytestPDB(object):
""" Pseudo PDB that defers to the real pdb. """ """ Pseudo PDB that defers to the real pdb. """
_pluginmanager = None _pluginmanager = None
_config = None _config = None
@ -64,7 +64,7 @@ class pytestPDB:
self._pdb_cls().set_trace(frame) self._pdb_cls().set_trace(frame)
class PdbInvoke: class PdbInvoke(object):
def pytest_exception_interact(self, node, call, report): def pytest_exception_interact(self, node, call, report):
capman = node.config.pluginmanager.getplugin("capturemanager") capman = node.config.pluginmanager.getplugin("capturemanager")
if capman: if capman:

View File

@ -25,6 +25,7 @@ DOCTEST_REPORT_CHOICES = (
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addini('doctest_optionflags', 'option flags for doctests', parser.addini('doctest_optionflags', 'option flags for doctests',
type="args", default=["ELLIPSIS"]) type="args", default=["ELLIPSIS"])
parser.addini("doctest_encoding", 'encoding used for doctest files', default="utf-8")
group = parser.getgroup("collect") group = parser.getgroup("collect")
group.addoption("--doctest-modules", group.addoption("--doctest-modules",
action="store_true", default=False, action="store_true", default=False,
@ -162,7 +163,6 @@ def get_optionflags(parent):
flag_acc |= flag_lookup_table[flag] flag_acc |= flag_lookup_table[flag]
return flag_acc return flag_acc
class DoctestTextfile(pytest.Module): class DoctestTextfile(pytest.Module):
obj = None obj = None
@ -171,7 +171,8 @@ class DoctestTextfile(pytest.Module):
# inspired by doctest.testfile; ideally we would use it directly, # inspired by doctest.testfile; ideally we would use it directly,
# but it doesn't support passing a custom checker # but it doesn't support passing a custom checker
text = self.fspath.read() encoding = self.config.getini("doctest_encoding")
text = self.fspath.read_text(encoding)
filename = str(self.fspath) filename = str(self.fspath)
name = self.fspath.basename name = self.fspath.basename
globs = {'__name__': '__main__'} globs = {'__name__': '__main__'}

View File

@ -222,7 +222,7 @@ def slice_items(items, ignore, scoped_argkeys_cache):
class FuncargnamesCompatAttr: class FuncargnamesCompatAttr(object):
""" helper class so that Metafunc, Function and FixtureRequest """ helper class so that Metafunc, Function and FixtureRequest
don't need to each define the "funcargnames" compatibility attribute. don't need to each define the "funcargnames" compatibility attribute.
""" """
@ -258,7 +258,7 @@ def fillfixtures(function):
def get_direct_param_fixture_func(request): def get_direct_param_fixture_func(request):
return request.param return request.param
class FuncFixtureInfo: class FuncFixtureInfo(object):
def __init__(self, argnames, names_closure, name2fixturedefs): def __init__(self, argnames, names_closure, name2fixturedefs):
self.argnames = argnames self.argnames = argnames
self.names_closure = names_closure self.names_closure = names_closure
@ -456,7 +456,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
fixturedef = self._getnextfixturedef(argname) fixturedef = self._getnextfixturedef(argname)
except FixtureLookupError: except FixtureLookupError:
if argname == "request": if argname == "request":
class PseudoFixtureDef: class PseudoFixtureDef(object):
cached_result = (self, [0], None) cached_result = (self, [0], None)
scope = "function" scope = "function"
return PseudoFixtureDef return PseudoFixtureDef
@ -723,7 +723,7 @@ def call_fixture_func(fixturefunc, request, kwargs):
return res return res
class FixtureDef: class FixtureDef(object):
""" A container for a factory definition. """ """ A container for a factory definition. """
def __init__(self, fixturemanager, baseid, argname, func, scope, params, def __init__(self, fixturemanager, baseid, argname, func, scope, params,
unittest=False, ids=None): unittest=False, ids=None):
@ -822,7 +822,7 @@ def pytest_fixture_setup(fixturedef, request):
return result return result
class FixtureFunctionMarker: class FixtureFunctionMarker(object):
def __init__(self, scope, params, autouse=False, ids=None, name=None): def __init__(self, scope, params, autouse=False, ids=None, name=None):
self.scope = scope self.scope = scope
self.params = params self.params = params
@ -909,7 +909,7 @@ def pytestconfig(request):
return request.config return request.config
class FixtureManager: class FixtureManager(object):
""" """
pytest fixtures definitions and information is stored and managed pytest fixtures definitions and information is stored and managed
from this class. from this class.

View File

@ -23,7 +23,7 @@ def pytest_addoption(parser):
group._addoption( group._addoption(
'-o', '--override-ini', nargs='*', dest="override_ini", '-o', '--override-ini', nargs='*', dest="override_ini",
action="append", action="append",
help="override config option, e.g. `-o xfail_strict=True`.") help="override config option with option=value style, e.g. `-o xfail_strict=True`.")
@pytest.hookimpl(hookwrapper=True) @pytest.hookimpl(hookwrapper=True)

View File

@ -157,9 +157,10 @@ def pytest_generate_tests(metafunc):
""" generate (multiple) parametrized calls to a test function.""" """ generate (multiple) parametrized calls to a test function."""
@hookspec(firstresult=True) @hookspec(firstresult=True)
def pytest_make_parametrize_id(config, val): def pytest_make_parametrize_id(config, val, argname):
"""Return a user-friendly string representation of the given ``val`` that will be used """Return a user-friendly string representation of the given ``val`` that will be used
by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``. by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``.
The parameter name is available as ``argname``, if required.
""" """
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------

View File

@ -8,12 +8,14 @@ Based on initial code from Ross Lawley.
# Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/ # Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/
# src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd # src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
import functools
import py import py
import os import os
import re import re
import sys import sys
import time import time
import pytest import pytest
from _pytest.config import filename_arg
# Python 2.X and 3.X compatibility # Python 2.X and 3.X compatibility
if sys.version_info[0] < 3: if sys.version_info[0] < 3:
@ -216,6 +218,7 @@ def pytest_addoption(parser):
action="store", action="store",
dest="xmlpath", dest="xmlpath",
metavar="path", metavar="path",
type=functools.partial(filename_arg, optname="--junitxml"),
default=None, default=None,
help="create junit-xml style report file at given path.") help="create junit-xml style report file at given path.")
group.addoption( group.addoption(

View File

@ -1,4 +1,5 @@
""" core implementation of testing process: init, session, runtest loop. """ """ core implementation of testing process: init, session, runtest loop. """
import functools
import os import os
import sys import sys
@ -11,6 +12,7 @@ try:
except ImportError: except ImportError:
from UserDict import DictMixin as MappingMixin from UserDict import DictMixin as MappingMixin
from _pytest.config import directory_arg
from _pytest.runner import collect_one_node from _pytest.runner import collect_one_node
tracebackcutdir = py.path.local(_pytest.__file__).dirpath() tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
@ -25,7 +27,7 @@ EXIT_NOTESTSCOLLECTED = 5
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addini("norecursedirs", "directory patterns to avoid for recursion", parser.addini("norecursedirs", "directory patterns to avoid for recursion",
type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg']) type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv'])
parser.addini("testpaths", "directories to search for tests when no files or directories are given in the command line.", parser.addini("testpaths", "directories to search for tests when no files or directories are given in the command line.",
type="args", default=[]) type="args", default=[])
#parser.addini("dirpatterns", #parser.addini("dirpatterns",
@ -58,7 +60,7 @@ def pytest_addoption(parser):
# when changing this to --conf-cut-dir, config.py Conftest.setinitial # when changing this to --conf-cut-dir, config.py Conftest.setinitial
# needs upgrading as well # needs upgrading as well
group.addoption('--confcutdir', dest="confcutdir", default=None, group.addoption('--confcutdir', dest="confcutdir", default=None,
metavar="dir", metavar="dir", type=functools.partial(directory_arg, optname="--confcutdir"),
help="only load conftest.py's relative to specified dir.") help="only load conftest.py's relative to specified dir.")
group.addoption('--noconftest', action="store_true", group.addoption('--noconftest', action="store_true",
dest="noconftest", default=False, dest="noconftest", default=False,
@ -177,7 +179,7 @@ def pytest_ignore_collect(path, config):
return False return False
class FSHookProxy: class FSHookProxy(object):
def __init__(self, fspath, pm, remove_mods): def __init__(self, fspath, pm, remove_mods):
self.fspath = fspath self.fspath = fspath
self.pm = pm self.pm = pm
@ -188,12 +190,22 @@ class FSHookProxy:
self.__dict__[name] = x self.__dict__[name] = x
return x return x
def compatproperty(name): class _CompatProperty(object):
def fget(self): def __init__(self, name):
# deprecated - use pytest.name self.name = name
return getattr(pytest, name)
def __get__(self, obj, owner):
if obj is None:
return self
# TODO: reenable in the features branch
# warnings.warn(
# "usage of {owner!r}.{name} is deprecated, please use pytest.{name} instead".format(
# name=self.name, owner=type(owner).__name__),
# PendingDeprecationWarning, stacklevel=2)
return getattr(pytest, self.name)
return property(fget)
class NodeKeywords(MappingMixin): class NodeKeywords(MappingMixin):
def __init__(self, node): def __init__(self, node):
@ -265,19 +277,23 @@ class Node(object):
""" fspath sensitive hook proxy used to call pytest hooks""" """ fspath sensitive hook proxy used to call pytest hooks"""
return self.session.gethookproxy(self.fspath) return self.session.gethookproxy(self.fspath)
Module = compatproperty("Module") Module = _CompatProperty("Module")
Class = compatproperty("Class") Class = _CompatProperty("Class")
Instance = compatproperty("Instance") Instance = _CompatProperty("Instance")
Function = compatproperty("Function") Function = _CompatProperty("Function")
File = compatproperty("File") File = _CompatProperty("File")
Item = compatproperty("Item") Item = _CompatProperty("Item")
def _getcustomclass(self, name): def _getcustomclass(self, name):
cls = getattr(self, name) maybe_compatprop = getattr(type(self), name)
if cls != getattr(pytest, name): if isinstance(maybe_compatprop, _CompatProperty):
py.log._apiwarn("2.0", "use of node.%s is deprecated, " return getattr(pytest, name)
"use pytest_pycollect_makeitem(...) to create custom " else:
"collection nodes" % name) cls = getattr(self, name)
# TODO: reenable in the features branch
# warnings.warn("use of node.%s is deprecated, "
# "use pytest_pycollect_makeitem(...) to create custom "
# "collection nodes" % name, category=DeprecationWarning)
return cls return cls
def __repr__(self): def __repr__(self):
@ -531,7 +547,6 @@ class Session(FSCollector):
def __init__(self, config): def __init__(self, config):
FSCollector.__init__(self, config.rootdir, parent=None, FSCollector.__init__(self, config.rootdir, parent=None,
config=config, session=self) config=config, session=self)
self._fs2hookproxy = {}
self.testsfailed = 0 self.testsfailed = 0
self.testscollected = 0 self.testscollected = 0
self.shouldstop = False self.shouldstop = False
@ -562,23 +577,18 @@ class Session(FSCollector):
return path in self._initialpaths return path in self._initialpaths
def gethookproxy(self, fspath): def gethookproxy(self, fspath):
try: # check if we have the common case of running
return self._fs2hookproxy[fspath] # hooks with all conftest.py filesall conftest.py
except KeyError: pm = self.config.pluginmanager
# check if we have the common case of running my_conftestmodules = pm._getconftestmodules(fspath)
# hooks with all conftest.py filesall conftest.py remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
pm = self.config.pluginmanager if remove_mods:
my_conftestmodules = pm._getconftestmodules(fspath) # one or more conftests are not in use at this fspath
remove_mods = pm._conftest_plugins.difference(my_conftestmodules) proxy = FSHookProxy(fspath, pm, remove_mods)
if remove_mods: else:
# one or more conftests are not in use at this fspath # all plugis are active for this fspath
proxy = FSHookProxy(fspath, pm, remove_mods) proxy = self.config.hook
else: return proxy
# all plugis are active for this fspath
proxy = self.config.hook
self._fs2hookproxy[fspath] = proxy
return proxy
def perform_collect(self, args=None, genitems=True): def perform_collect(self, args=None, genitems=True):
hook = self.config.hook hook = self.config.hook
@ -700,10 +710,9 @@ class Session(FSCollector):
path = self.config.invocation_dir.join(relpath, abs=True) path = self.config.invocation_dir.join(relpath, abs=True)
if not path.check(): if not path.check():
if self.config.option.pyargs: if self.config.option.pyargs:
msg = "file or package not found: " raise pytest.UsageError("file or package not found: " + arg + " (missing __init__.py?)")
else: else:
msg = "file not found: " raise pytest.UsageError("file not found: " + arg)
raise pytest.UsageError(msg + arg)
parts[0] = path parts[0] = path
return parts return parts

View File

@ -99,7 +99,7 @@ def pytest_collection_modifyitems(items, config):
items[:] = remaining items[:] = remaining
class MarkMapping: class MarkMapping(object):
"""Provides a local mapping for markers where item access """Provides a local mapping for markers where item access
resolves to True if the marker is present. """ resolves to True if the marker is present. """
def __init__(self, keywords): def __init__(self, keywords):
@ -113,7 +113,7 @@ class MarkMapping:
return name in self._mymarks return name in self._mymarks
class KeywordMapping: class KeywordMapping(object):
"""Provides a local mapping for keywords. """Provides a local mapping for keywords.
Given a list of names, map any substring of one of these names to True. Given a list of names, map any substring of one of these names to True.
""" """
@ -173,7 +173,7 @@ def pytest_configure(config):
pytest.mark._config = config pytest.mark._config = config
class MarkGenerator: class MarkGenerator(object):
""" Factory for :class:`MarkDecorator` objects - exposed as """ Factory for :class:`MarkDecorator` objects - exposed as
a ``pytest.mark`` singleton instance. Example:: a ``pytest.mark`` singleton instance. Example::
@ -210,7 +210,7 @@ def istestfunc(func):
return hasattr(func, "__call__") and \ return hasattr(func, "__call__") and \
getattr(func, "__name__", "<lambda>") != "<lambda>" getattr(func, "__name__", "<lambda>") != "<lambda>"
class MarkDecorator: class MarkDecorator(object):
""" A decorator for test functions and test classes. When applied """ A decorator for test functions and test classes. When applied
it will create :class:`MarkInfo` objects which may be it will create :class:`MarkInfo` objects which may be
:ref:`retrieved by hooks as item keywords <excontrolskip>`. :ref:`retrieved by hooks as item keywords <excontrolskip>`.

View File

@ -11,7 +11,7 @@ RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
@pytest.fixture @pytest.fixture
def monkeypatch(request): def monkeypatch():
"""The returned ``monkeypatch`` fixture provides these """The returned ``monkeypatch`` fixture provides these
helper methods to modify objects, dictionaries or os.environ:: helper methods to modify objects, dictionaries or os.environ::
@ -30,8 +30,8 @@ def monkeypatch(request):
will be raised if the set/deletion operation has no target. will be raised if the set/deletion operation has no target.
""" """
mpatch = MonkeyPatch() mpatch = MonkeyPatch()
request.addfinalizer(mpatch.undo) yield mpatch
return mpatch mpatch.undo()
def resolve(name): def resolve(name):
@ -88,7 +88,7 @@ def derive_importpath(import_path, raising):
return attr, target return attr, target
class Notset: class Notset(object):
def __repr__(self): def __repr__(self):
return "<notset>" return "<notset>"
@ -96,7 +96,7 @@ class Notset:
notset = Notset() notset = Notset()
class MonkeyPatch: class MonkeyPatch(object):
""" Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes. """ Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes.
""" """

View File

@ -165,7 +165,7 @@ def _pytest(request):
""" """
return PytestArg(request) return PytestArg(request)
class PytestArg: class PytestArg(object):
def __init__(self, request): def __init__(self, request):
self.request = request self.request = request
@ -180,7 +180,7 @@ def get_public_names(l):
return [x for x in l if x[0] != "_"] return [x for x in l if x[0] != "_"]
class ParsedCall: class ParsedCall(object):
def __init__(self, name, kwargs): def __init__(self, name, kwargs):
self.__dict__.update(kwargs) self.__dict__.update(kwargs)
self._name = name self._name = name
@ -191,7 +191,7 @@ class ParsedCall:
return "<ParsedCall %r(**%r)>" %(self._name, d) return "<ParsedCall %r(**%r)>" %(self._name, d)
class HookRecorder: class HookRecorder(object):
"""Record all hooks called in a plugin manager. """Record all hooks called in a plugin manager.
This wraps all the hook calls in the plugin manager, recording This wraps all the hook calls in the plugin manager, recording
@ -335,7 +335,7 @@ def testdir(request, tmpdir_factory):
rex_outcome = re.compile("(\d+) ([\w-]+)") rex_outcome = re.compile("(\d+) ([\w-]+)")
class RunResult: class RunResult(object):
"""The result of running a command. """The result of running a command.
Attributes: Attributes:
@ -369,6 +369,7 @@ class RunResult:
for num, cat in outcomes: for num, cat in outcomes:
d[cat] = int(num) d[cat] = int(num)
return d return d
raise ValueError("Pytest terminal report not found")
def assert_outcomes(self, passed=0, skipped=0, failed=0): def assert_outcomes(self, passed=0, skipped=0, failed=0):
""" assert that the specified outcomes appear with the respective """ assert that the specified outcomes appear with the respective
@ -380,7 +381,7 @@ class RunResult:
class Testdir: class Testdir(object):
"""Temporary test directory with tools to test/run pytest itself. """Temporary test directory with tools to test/run pytest itself.
This is based on the ``tmpdir`` fixture but provides a number of This is based on the ``tmpdir`` fixture but provides a number of
@ -449,9 +450,9 @@ class Testdir:
the module is re-imported. the module is re-imported.
""" """
for name in set(sys.modules).difference(self._savemodulekeys): for name in set(sys.modules).difference(self._savemodulekeys):
# it seems zope.interfaces is keeping some state # zope.interface (used by twisted-related tests) keeps internal
# (used by twisted related tests) # state and can't be deleted
if name != "zope.interface": if not name.startswith("zope.interface"):
del sys.modules[name] del sys.modules[name]
def make_hook_recorder(self, pluginmanager): def make_hook_recorder(self, pluginmanager):
@ -471,7 +472,7 @@ class Testdir:
if not hasattr(self, '_olddir'): if not hasattr(self, '_olddir'):
self._olddir = old self._olddir = old
def _makefile(self, ext, args, kwargs): def _makefile(self, ext, args, kwargs, encoding="utf-8"):
items = list(kwargs.items()) items = list(kwargs.items())
if args: if args:
source = py.builtin._totext("\n").join( source = py.builtin._totext("\n").join(
@ -481,6 +482,7 @@ class Testdir:
ret = None ret = None
for name, value in items: for name, value in items:
p = self.tmpdir.join(name).new(ext=ext) p = self.tmpdir.join(name).new(ext=ext)
p.dirpath().ensure_dir()
source = Source(value) source = Source(value)
def my_totext(s, encoding="utf-8"): def my_totext(s, encoding="utf-8"):
@ -490,7 +492,7 @@ class Testdir:
source_unicode = "\n".join([my_totext(line) for line in source.lines]) source_unicode = "\n".join([my_totext(line) for line in source.lines])
source = py.builtin._totext(source_unicode) source = py.builtin._totext(source_unicode)
content = source.strip().encode("utf-8") # + "\n" content = source.strip().encode(encoding) # + "\n"
#content = content.rstrip() + "\n" #content = content.rstrip() + "\n"
p.write(content, "wb") p.write(content, "wb")
if ret is None: if ret is None:
@ -706,7 +708,7 @@ class Testdir:
rec = [] rec = []
class Collect: class Collect(object):
def pytest_configure(x, config): def pytest_configure(x, config):
rec.append(self.make_hook_recorder(config.pluginmanager)) rec.append(self.make_hook_recorder(config.pluginmanager))
@ -717,7 +719,7 @@ class Testdir:
if len(rec) == 1: if len(rec) == 1:
reprec = rec.pop() reprec = rec.pop()
else: else:
class reprec: class reprec(object):
pass pass
reprec.ret = ret reprec.ret = ret
@ -741,13 +743,13 @@ class Testdir:
reprec = self.inline_run(*args, **kwargs) reprec = self.inline_run(*args, **kwargs)
except SystemExit as e: except SystemExit as e:
class reprec: class reprec(object):
ret = e.args[0] ret = e.args[0]
except Exception: except Exception:
traceback.print_exc() traceback.print_exc()
class reprec: class reprec(object):
ret = 3 ret = 3
finally: finally:
out, err = capture.reset() out, err = capture.reset()
@ -1032,7 +1034,7 @@ def getdecoded(out):
py.io.saferepr(out),) py.io.saferepr(out),)
class LineComp: class LineComp(object):
def __init__(self): def __init__(self):
self.stringio = py.io.TextIO() self.stringio = py.io.TextIO()
@ -1048,7 +1050,7 @@ class LineComp:
return LineMatcher(lines1).fnmatch_lines(lines2) return LineMatcher(lines1).fnmatch_lines(lines2)
class LineMatcher: class LineMatcher(object):
"""Flexible matching of text. """Flexible matching of text.
This is a convenience class to test large texts like the output of This is a convenience class to test large texts like the output of

View File

@ -197,7 +197,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
res = list(collector._genfunctions(name, obj)) res = list(collector._genfunctions(name, obj))
outcome.force_result(res) outcome.force_result(res)
def pytest_make_parametrize_id(config, val): def pytest_make_parametrize_id(config, val, argname=None):
return None return None
@ -503,6 +503,8 @@ def _get_xunit_func(obj, name):
class Class(PyCollector): class Class(PyCollector):
""" Collector for test methods. """ """ Collector for test methods. """
def collect(self): def collect(self):
if not safe_getattr(self.obj, "__test__", True):
return []
if hasinit(self.obj): if hasinit(self.obj):
self.warn("C1", "cannot collect test class %r because it has a " self.warn("C1", "cannot collect test class %r because it has a "
"__init__ constructor" % self.obj.__name__) "__init__ constructor" % self.obj.__name__)
@ -926,15 +928,21 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
def _idval(val, argname, idx, idfn, config=None): def _idval(val, argname, idx, idfn, config=None):
if idfn: if idfn:
s = None
try: try:
s = idfn(val) s = idfn(val)
if s:
return _escape_strings(s)
except Exception: except Exception:
pass # See issue https://github.com/pytest-dev/pytest/issues/2169
import warnings
msg = "Raised while trying to determine id of parameter %s at position %d." % (argname, idx)
msg += '\nUpdate your code as this will raise an error in pytest-4.0.'
warnings.warn(msg)
if s:
return _escape_strings(s)
if config: if config:
hook_id = config.hook.pytest_make_parametrize_id(config=config, val=val) hook_id = config.hook.pytest_make_parametrize_id(
config=config, val=val, argname=argname)
if hook_id: if hook_id:
return hook_id return hook_id
@ -1126,7 +1134,7 @@ def raises(expected_exception, *args, **kwargs):
>>> with raises(ValueError) as exc_info: >>> with raises(ValueError) as exc_info:
... if value > 10: ... if value > 10:
... raise ValueError("value must be <= 10") ... raise ValueError("value must be <= 10")
... assert str(exc_info.value) == "value must be <= 10" # this will not execute ... assert exc_info.type == ValueError # this will not execute
Instead, the following approach must be taken (note the difference in Instead, the following approach must be taken (note the difference in
scope):: scope)::
@ -1135,7 +1143,16 @@ def raises(expected_exception, *args, **kwargs):
... if value > 10: ... if value > 10:
... raise ValueError("value must be <= 10") ... raise ValueError("value must be <= 10")
... ...
>>> assert str(exc_info.value) == "value must be <= 10" >>> assert exc_info.type == ValueError
Or you can use the keyword argument ``match`` to assert that the
exception matches a text or regex::
>>> with raises(ValueError, match='must be 0 or None'):
... raise ValueError("value must be 0 or None")
>>> with raises(ValueError, match=r'must be \d+$'):
... raise ValueError("value must be 42")
Or you can specify a callable by passing a to-be-called lambda:: Or you can specify a callable by passing a to-be-called lambda::
@ -1186,11 +1203,15 @@ def raises(expected_exception, *args, **kwargs):
raise TypeError(msg % type(expected_exception)) raise TypeError(msg % type(expected_exception))
message = "DID NOT RAISE {0}".format(expected_exception) message = "DID NOT RAISE {0}".format(expected_exception)
match_expr = None
if not args: if not args:
if "message" in kwargs: if "message" in kwargs:
message = kwargs.pop("message") message = kwargs.pop("message")
return RaisesContext(expected_exception, message) if "match" in kwargs:
match_expr = kwargs.pop("match")
message += " matching '{0}'".format(match_expr)
return RaisesContext(expected_exception, message, match_expr)
elif isinstance(args[0], str): elif isinstance(args[0], str):
code, = args code, = args
assert isinstance(code, str) assert isinstance(code, str)
@ -1214,9 +1235,10 @@ def raises(expected_exception, *args, **kwargs):
pytest.fail(message) pytest.fail(message)
class RaisesContext(object): class RaisesContext(object):
def __init__(self, expected_exception, message): def __init__(self, expected_exception, message, match_expr):
self.expected_exception = expected_exception self.expected_exception = expected_exception
self.message = message self.message = message
self.match_expr = match_expr
self.excinfo = None self.excinfo = None
def __enter__(self): def __enter__(self):
@ -1238,6 +1260,8 @@ class RaisesContext(object):
suppress_exception = issubclass(self.excinfo.type, self.expected_exception) suppress_exception = issubclass(self.excinfo.type, self.expected_exception)
if sys.version_info[0] == 2 and suppress_exception: if sys.version_info[0] == 2 and suppress_exception:
sys.exc_clear() sys.exc_clear()
if self.match_expr:
self.excinfo.match(self.match_expr)
return suppress_exception return suppress_exception
@ -1414,6 +1438,9 @@ class ApproxNonIterable(object):
self.rel = rel self.rel = rel
def __repr__(self): def __repr__(self):
if isinstance(self.expected, complex):
return str(self.expected)
# Infinities aren't compared using tolerances, so don't show a # Infinities aren't compared using tolerances, so don't show a
# tolerance. # tolerance.
if math.isinf(self.expected): if math.isinf(self.expected):
@ -1426,16 +1453,10 @@ class ApproxNonIterable(object):
except ValueError: except ValueError:
vetted_tolerance = '???' vetted_tolerance = '???'
plus_minus = u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
# In python2, __repr__() must return a string (i.e. not a unicode
# object). In python3, __repr__() must return a unicode object
# (although now strings are unicode objects and bytes are what
# strings were).
if sys.version_info[0] == 2: if sys.version_info[0] == 2:
return plus_minus.encode('utf-8') return '{0} +- {1}'.format(self.expected, vetted_tolerance)
else: else:
return plus_minus return u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
def __eq__(self, actual): def __eq__(self, actual):
# Short-circuit exact equality. # Short-circuit exact equality.

View File

@ -216,6 +216,10 @@ class WarningsChecker(WarningsRecorder):
# only check if we're not currently handling an exception # only check if we're not currently handling an exception
if all(a is None for a in exc_info): if all(a is None for a in exc_info):
if self.expected_warning is not None: if self.expected_warning is not None:
if not any(r.category in self.expected_warning for r in self): if not any(issubclass(r.category, self.expected_warning)
for r in self):
__tracebackhide__ = True __tracebackhide__ = True
pytest.fail("DID NOT WARN") pytest.fail("DID NOT WARN. No warnings of type {0} was emitted. "
"The list of emitted warnings is: {1}.".format(
self.expected_warning,
[each.message for each in self]))

View File

@ -55,7 +55,7 @@ def pytest_sessionstart(session):
def pytest_sessionfinish(session): def pytest_sessionfinish(session):
session._setupstate.teardown_all() session._setupstate.teardown_all()
class NodeInfo: class NodeInfo(object):
def __init__(self, location): def __init__(self, location):
self.location = location self.location = location
@ -150,7 +150,7 @@ def call_runtest_hook(item, when, **kwds):
ihook = getattr(item.ihook, hookname) ihook = getattr(item.ihook, hookname)
return CallInfo(lambda: ihook(item=item, **kwds), when=when) return CallInfo(lambda: ihook(item=item, **kwds), when=when)
class CallInfo: class CallInfo(object):
""" Result/Exception info a function invocation. """ """ Result/Exception info a function invocation. """
#: None or ExceptionInfo object. #: None or ExceptionInfo object.
excinfo = None excinfo = None

View File

@ -5,9 +5,9 @@ import sys
def pytest_addoption(parser): def pytest_addoption(parser):
group = parser.getgroup("debugconfig") group = parser.getgroup("debugconfig")
group.addoption('--setuponly', '--setup-only', action="store_true", group.addoption('--setuponly', '--setup-only', action="store_true",
help="only setup fixtures, don't execute the tests.") help="only setup fixtures, do not execute tests.")
group.addoption('--setupshow', '--setup-show', action="store_true", group.addoption('--setupshow', '--setup-show', action="store_true",
help="show setup fixtures while executing the tests.") help="show setup of fixtures while executing tests.")
@pytest.hookimpl(hookwrapper=True) @pytest.hookimpl(hookwrapper=True)

View File

@ -46,7 +46,7 @@ def pytest_configure(config):
) )
config.addinivalue_line("markers", config.addinivalue_line("markers",
"xfail(condition, reason=None, run=True, raises=None, strict=False): " "xfail(condition, reason=None, run=True, raises=None, strict=False): "
"mark the the test function as an expected failure if eval(condition) " "mark the test function as an expected failure if eval(condition) "
"has a True value. Optionally specify a reason for better reporting " "has a True value. Optionally specify a reason for better reporting "
"and run=False if you don't even want to execute the test function. " "and run=False if you don't even want to execute the test function. "
"If only specific exception(s) are expected, you can list them in " "If only specific exception(s) are expected, you can list them in "
@ -72,7 +72,7 @@ def xfail(reason=""):
xfail.Exception = XFailed xfail.Exception = XFailed
class MarkEvaluator: class MarkEvaluator(object):
def __init__(self, item, name): def __init__(self, item, name):
self.item = item self.item = item
self.name = name self.name = name

View File

@ -80,7 +80,7 @@ def pytest_report_teststatus(report):
letter = "f" letter = "f"
return report.outcome, letter, report.outcome.upper() return report.outcome, letter, report.outcome.upper()
class WarningReport: class WarningReport(object):
def __init__(self, code, message, nodeid=None, fslocation=None): def __init__(self, code, message, nodeid=None, fslocation=None):
self.code = code self.code = code
self.message = message self.message = message
@ -88,7 +88,7 @@ class WarningReport:
self.fslocation = fslocation self.fslocation = fslocation
class TerminalReporter: class TerminalReporter(object):
def __init__(self, config, file=None): def __init__(self, config, file=None):
import _pytest.config import _pytest.config
self.config = config self.config = config

View File

@ -6,7 +6,7 @@ import py
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
class TempdirFactory: class TempdirFactory(object):
"""Factory for temporary directories under the common base temp directory. """Factory for temporary directories under the common base temp directory.
The base directory can be configured using the ``--basetemp`` option. The base directory can be configured using the ``--basetemp`` option.

View File

@ -75,7 +75,7 @@ __all__ = ["PluginManager", "PluginValidationError", "HookCallError",
_py3 = sys.version_info > (3, 0) _py3 = sys.version_info > (3, 0)
class HookspecMarker: class HookspecMarker(object):
""" Decorator helper class for marking functions as hook specifications. """ Decorator helper class for marking functions as hook specifications.
You can instantiate it with a project_name to get a decorator. You can instantiate it with a project_name to get a decorator.
@ -113,7 +113,7 @@ class HookspecMarker:
return setattr_hookspec_opts return setattr_hookspec_opts
class HookimplMarker: class HookimplMarker(object):
""" Decorator helper class for marking functions as hook implementations. """ Decorator helper class for marking functions as hook implementations.
You can instantiate with a project_name to get a decorator. You can instantiate with a project_name to get a decorator.
@ -167,7 +167,7 @@ def normalize_hookimpl_opts(opts):
opts.setdefault("optionalhook", False) opts.setdefault("optionalhook", False)
class _TagTracer: class _TagTracer(object):
def __init__(self): def __init__(self):
self._tag2proc = {} self._tag2proc = {}
self.writer = None self.writer = None
@ -214,7 +214,7 @@ class _TagTracer:
self._tag2proc[tags] = processor self._tag2proc[tags] = processor
class _TagTracerSub: class _TagTracerSub(object):
def __init__(self, root, tags): def __init__(self, root, tags):
self.root = root self.root = root
self.tags = tags self.tags = tags
@ -254,7 +254,7 @@ def _wrapped_call(wrap_controller, func):
return call_outcome.get_result() return call_outcome.get_result()
class _CallOutcome: class _CallOutcome(object):
""" Outcome of a function call, either an exception or a proper result. """ Outcome of a function call, either an exception or a proper result.
Calling the ``get_result`` method will return the result or reraise Calling the ``get_result`` method will return the result or reraise
the exception raised when the function was called. """ the exception raised when the function was called. """
@ -286,7 +286,7 @@ def _reraise(cls, val, tb):
""") """)
class _TracedHookExecution: class _TracedHookExecution(object):
def __init__(self, pluginmanager, before, after): def __init__(self, pluginmanager, before, after):
self.pluginmanager = pluginmanager self.pluginmanager = pluginmanager
self.before = before self.before = before
@ -580,7 +580,7 @@ class PluginManager(object):
return orig return orig
class _MultiCall: class _MultiCall(object):
""" execute a call into multiple python functions/methods. """ """ execute a call into multiple python functions/methods. """
# XXX note that the __multicall__ argument is supported only # XXX note that the __multicall__ argument is supported only
@ -673,7 +673,7 @@ def varnames(func, startindex=None):
return x return x
class _HookRelay: class _HookRelay(object):
""" hook holder object for performing 1:N hook calls where N is the number """ hook holder object for performing 1:N hook calls where N is the number
of registered plugins. of registered plugins.
@ -770,7 +770,7 @@ class _HookCaller(object):
proc(res[0]) proc(res[0])
class HookImpl: class HookImpl(object):
def __init__(self, plugin, plugin_name, function, hook_impl_opts): def __init__(self, plugin, plugin_name, function, hook_impl_opts):
self.function = function self.function = function
self.argnames = varnames(self.function) self.argnames = varnames(self.function)

View File

@ -6,6 +6,8 @@ environment:
# https://www.appveyor.com/docs/build-configuration#secure-variables # https://www.appveyor.com/docs/build-configuration#secure-variables
matrix: matrix:
# coveralls is not in the default env list
- TOXENV: "coveralls"
# note: please use "tox --listenvs" to populate the build matrix below # note: please use "tox --listenvs" to populate the build matrix below
- TOXENV: "linting" - TOXENV: "linting"
- TOXENV: "py26" - TOXENV: "py26"
@ -13,6 +15,7 @@ environment:
- TOXENV: "py33" - TOXENV: "py33"
- TOXENV: "py34" - TOXENV: "py34"
- TOXENV: "py35" - TOXENV: "py35"
- TOXENV: "py36"
- TOXENV: "pypy" - TOXENV: "pypy"
- TOXENV: "py27-pexpect" - TOXENV: "py27-pexpect"
- TOXENV: "py27-xdist" - TOXENV: "py27-xdist"
@ -29,14 +32,11 @@ install:
- echo Installed Pythons - echo Installed Pythons
- dir c:\Python* - dir c:\Python*
- if "%TOXENV%" == "pypy" scripts\install-pypy.bat - if "%TOXENV%" == "pypy" call scripts\install-pypy.bat
- C:\Python35\python -m pip install tox - C:\Python35\python -m pip install tox
build: false # Not a C# project, build stuff at the test step instead. build: false # Not a C# project, build stuff at the test step instead.
test_script: test_script:
- C:\Python35\python -m tox - call scripts\call-tox.bat
# coveralls is not in tox's envlist, plus for PRs the secure variable
# is not defined so we have to check for it
- if defined COVERALLS_REPO_TOKEN C:\Python35\python -m tox -e coveralls

View File

@ -6,6 +6,8 @@ Release announcements
:maxdepth: 2 :maxdepth: 2
release-3.0.6
release-3.0.5
release-3.0.4 release-3.0.4
release-3.0.3 release-3.0.3
release-3.0.2 release-3.0.2

View File

@ -63,9 +63,9 @@ Changes between 2.0.1 and 2.0.2
this. this.
- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular - fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular
thanks to Laura Creighton who also revieved parts of the documentation. thanks to Laura Creighton who also reviewed parts of the documentation.
- fix slighly wrong output of verbose progress reporting for classes - fix slightly wrong output of verbose progress reporting for classes
(thanks Amaury) (thanks Amaury)
- more precise (avoiding of) deprecation warnings for node.Class|Function accesses - more precise (avoiding of) deprecation warnings for node.Class|Function accesses

View File

@ -13,7 +13,7 @@ If you want to install or upgrade pytest, just type one of::
easy_install -U pytest easy_install -U pytest
There also is a bugfix release 1.6 of pytest-xdist, the plugin There also is a bugfix release 1.6 of pytest-xdist, the plugin
that enables seemless distributed and "looponfail" testing for Python. that enables seamless distributed and "looponfail" testing for Python.
best, best,
holger krekel holger krekel
@ -33,7 +33,7 @@ Changes between 2.0.2 and 2.0.3
- don't require zlib (and other libs) for genscript plugin without - don't require zlib (and other libs) for genscript plugin without
--genscript actually being used. --genscript actually being used.
- speed up skips (by not doing a full traceback represenation - speed up skips (by not doing a full traceback representation
internally) internally)
- fix issue37: avoid invalid characters in junitxml's output - fix issue37: avoid invalid characters in junitxml's output

View File

@ -2,7 +2,7 @@ pytest-2.2.1: bug fixes, perfect teardowns
=========================================================================== ===========================================================================
pytest-2.2.1 is a minor backward-compatible release of the the py.test pytest-2.2.1 is a minor backward-compatible release of the py.test
testing tool. It contains bug fixes and little improvements, including testing tool. It contains bug fixes and little improvements, including
documentation fixes. If you are using the distributed testing documentation fixes. If you are using the distributed testing
pluginmake sure to upgrade it to pytest-xdist-1.8. pluginmake sure to upgrade it to pytest-xdist-1.8.

View File

@ -29,7 +29,7 @@ Changes between 2.2.3 and 2.2.4
- fix issue with unittest: now @unittest.expectedFailure markers should - fix issue with unittest: now @unittest.expectedFailure markers should
be processed correctly (you can also use @pytest.mark markers) be processed correctly (you can also use @pytest.mark markers)
- document integration with the extended distribute/setuptools test commands - document integration with the extended distribute/setuptools test commands
- fix issue 140: propperly get the real functions - fix issue 140: properly get the real functions
of bound classmethods for setup/teardown_class of bound classmethods for setup/teardown_class
- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net - fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net
- fix issue #143: call unconfigure/sessionfinish always when - fix issue #143: call unconfigure/sessionfinish always when

View File

@ -89,7 +89,7 @@ Changes between 2.2.4 and 2.3.0
- fix issue128: show captured output when capsys/capfd are used - fix issue128: show captured output when capsys/capfd are used
- fix issue179: propperly show the dependency chain of factories - fix issue179: properly show the dependency chain of factories
- pluginmanager.register(...) now raises ValueError if the - pluginmanager.register(...) now raises ValueError if the
plugin has been already registered or the name is taken plugin has been already registered or the name is taken
@ -130,5 +130,5 @@ Changes between 2.2.4 and 2.3.0
- don't show deselected reason line if there is none - don't show deselected reason line if there is none
- py.test -vv will show all of assert comparisations instead of truncating - py.test -vv will show all of assert comparisons instead of truncating

View File

@ -1,7 +1,7 @@
pytest-2.3.2: some fixes and more traceback-printing speed pytest-2.3.2: some fixes and more traceback-printing speed
=========================================================================== ===========================================================================
pytest-2.3.2 is a another stabilization release: pytest-2.3.2 is another stabilization release:
- issue 205: fixes a regression with conftest detection - issue 205: fixes a regression with conftest detection
- issue 208/29: fixes traceback-printing speed in some bad cases - issue 208/29: fixes traceback-printing speed in some bad cases

View File

@ -1,7 +1,7 @@
pytest-2.3.3: integration fixes, py24 suport, ``*/**`` shown in traceback pytest-2.3.3: integration fixes, py24 support, ``*/**`` shown in traceback
=========================================================================== ===========================================================================
pytest-2.3.3 is a another stabilization release of the py.test tool pytest-2.3.3 is another stabilization release of the py.test tool
which offers uebersimple assertions, scalable fixture mechanisms which offers uebersimple assertions, scalable fixture mechanisms
and deep customization for testing with Python. Particularly, and deep customization for testing with Python. Particularly,
this release provides: this release provides:
@ -46,7 +46,7 @@ Changes between 2.3.2 and 2.3.3
- fix issue209 - reintroduce python2.4 support by depending on newer - fix issue209 - reintroduce python2.4 support by depending on newer
pylib which re-introduced statement-finding for pre-AST interpreters pylib which re-introduced statement-finding for pre-AST interpreters
- nose support: only call setup if its a callable, thanks Andrew - nose support: only call setup if it's a callable, thanks Andrew
Taumoefolau Taumoefolau
- fix issue219 - add py2.4-3.3 classifiers to TROVE list - fix issue219 - add py2.4-3.3 classifiers to TROVE list

View File

@ -44,11 +44,11 @@ Changes between 2.3.4 and 2.3.5
(thanks Adam Goucher) (thanks Adam Goucher)
- Issue 265 - integrate nose setup/teardown with setupstate - Issue 265 - integrate nose setup/teardown with setupstate
so it doesnt try to teardown if it did not setup so it doesn't try to teardown if it did not setup
- issue 271 - dont write junitxml on slave nodes - issue 271 - don't write junitxml on slave nodes
- Issue 274 - dont try to show full doctest example - Issue 274 - don't try to show full doctest example
when doctest does not know the example location when doctest does not know the example location
- issue 280 - disable assertion rewriting on buggy CPython 2.6.0 - issue 280 - disable assertion rewriting on buggy CPython 2.6.0
@ -84,7 +84,7 @@ Changes between 2.3.4 and 2.3.5
- allow to specify prefixes starting with "_" when - allow to specify prefixes starting with "_" when
customizing python_functions test discovery. (thanks Graham Horler) customizing python_functions test discovery. (thanks Graham Horler)
- improve PYTEST_DEBUG tracing output by puting - improve PYTEST_DEBUG tracing output by putting
extra data on a new lines with additional indent extra data on a new lines with additional indent
- ensure OutcomeExceptions like skip/fail have initialized exception attributes - ensure OutcomeExceptions like skip/fail have initialized exception attributes

View File

@ -36,7 +36,7 @@ a full list of details. A few feature highlights:
- reporting: color the last line red or green depending if - reporting: color the last line red or green depending if
failures/errors occurred or everything passed. failures/errors occurred or everything passed.
The documentation has been updated to accomodate the changes, The documentation has been updated to accommodate the changes,
see `http://pytest.org <http://pytest.org>`_ see `http://pytest.org <http://pytest.org>`_
To install or upgrade pytest:: To install or upgrade pytest::
@ -118,7 +118,7 @@ new features:
- fix issue322: tearDownClass is not run if setUpClass failed. Thanks - fix issue322: tearDownClass is not run if setUpClass failed. Thanks
Mathieu Agopian for the initial fix. Also make all of pytest/nose Mathieu Agopian for the initial fix. Also make all of pytest/nose
finalizer mimick the same generic behaviour: if a setupX exists and finalizer mimic the same generic behaviour: if a setupX exists and
fails, don't run teardownX. This internally introduces a new method fails, don't run teardownX. This internally introduces a new method
"node.addfinalizer()" helper which can only be called during the setup "node.addfinalizer()" helper which can only be called during the setup
phase of a node. phase of a node.

View File

@ -70,7 +70,7 @@ holger krekel
to problems for more than >966 non-function scoped parameters). to problems for more than >966 non-function scoped parameters).
- fix issue290 - there is preliminary support now for parametrizing - fix issue290 - there is preliminary support now for parametrizing
with repeated same values (sometimes useful to to test if calling with repeated same values (sometimes useful to test if calling
a second time works as with the first time). a second time works as with the first time).
- close issue240 - document precisely how pytest module importing - close issue240 - document precisely how pytest module importing
@ -149,7 +149,7 @@ holger krekel
would not work correctly because pytest assumes @pytest.mark.some would not work correctly because pytest assumes @pytest.mark.some
gets a function to be decorated already. We now at least detect if this gets a function to be decorated already. We now at least detect if this
arg is an lambda and thus the example will work. Thanks Alex Gaynor arg is a lambda and thus the example will work. Thanks Alex Gaynor
for bringing it up. for bringing it up.
- xfail a test on pypy that checks wrong encoding/ascii (pypy does - xfail a test on pypy that checks wrong encoding/ascii (pypy does

View File

@ -60,5 +60,5 @@ holger krekel
- fix issue429: comparing byte strings with non-ascii chars in assert - fix issue429: comparing byte strings with non-ascii chars in assert
expressions now work better. Thanks Floris Bruynooghe. expressions now work better. Thanks Floris Bruynooghe.
- make capfd/capsys.capture private, its unused and shouldnt be exposed - make capfd/capsys.capture private, its unused and shouldn't be exposed

View File

@ -42,7 +42,7 @@ Changes 2.6.3
- fix conftest related fixture visibility issue: when running with a - fix conftest related fixture visibility issue: when running with a
CWD outside of a test package pytest would get fixture discovery wrong. CWD outside of a test package pytest would get fixture discovery wrong.
Thanks to Wolfgang Schnerring for figuring out a reproducable example. Thanks to Wolfgang Schnerring for figuring out a reproducible example.
- Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the - Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the
timeout when interactively entering pdb). Thanks Wolfgang Schnerring. timeout when interactively entering pdb). Thanks Wolfgang Schnerring.

View File

@ -32,7 +32,7 @@ The py.test Development Team
explanations. Thanks Carl Meyer for the report and test case. explanations. Thanks Carl Meyer for the report and test case.
- fix issue553: properly handling inspect.getsourcelines failures in - fix issue553: properly handling inspect.getsourcelines failures in
FixtureLookupError which would lead to to an internal error, FixtureLookupError which would lead to an internal error,
obfuscating the original problem. Thanks talljosh for initial obfuscating the original problem. Thanks talljosh for initial
diagnose/patch and Bruno Oliveira for final patch. diagnose/patch and Bruno Oliveira for final patch.

View File

@ -46,7 +46,7 @@ The py.test Development Team
Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_ Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_
for PR the (`#1524`_). for PR the (`#1524`_).
* Fix win32 path issue when puttinging custom config file with absolute path * Fix win32 path issue when putting custom config file with absolute path
in ``pytest.main("-c your_absolute_path")``. in ``pytest.main("-c your_absolute_path")``.
* Fix maximum recursion depth detection when raised error class is not aware * Fix maximum recursion depth detection when raised error class is not aware

View File

@ -0,0 +1,27 @@
pytest-3.0.5
============
pytest 3.0.5 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The changelog is available at http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Ana Vojnovic
* Bruno Oliveira
* Daniel Hahler
* Duncan Betts
* Igor Starikov
* Ismail
* Luke Murphy
* Ned Batchelder
* Ronny Pfannschmidt
* Sebastian Ramacher
* nmundar
Happy testing,
The pytest Development Team

View File

@ -0,0 +1,33 @@
pytest-3.0.6
============
pytest 3.0.6 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 http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Andreas Pelme
* Bruno Oliveira
* Dmitry Malinovsky
* Eli Boyarski
* Jakub Wilk
* Jeff Widman
* Loïc Estève
* Luke Murphy
* Miro Hrončok
* Oscar Hellström
* Peter Heatwole
* Philippe Ombredanne
* Ronny Pfannschmidt
* Rutger Prins
* Stefan Scherfke
Happy testing,
The pytest Development Team

View File

@ -26,7 +26,7 @@ you will see the return value of the function call::
$ pytest test_assert1.py $ pytest test_assert1.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items
@ -170,7 +170,7 @@ if you run this module::
$ pytest test_assert2.py $ pytest test_assert2.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items
@ -223,7 +223,7 @@ provides an alternative explanation for ``Foo`` objects::
now, given this test module:: now, given this test module::
# content of test_foocompare.py # content of test_foocompare.py
class Foo: class Foo(object):
def __init__(self, val): def __init__(self, val):
self.val = val self.val = val
@ -262,50 +262,20 @@ Advanced assertion introspection
.. versionadded:: 2.1 .. versionadded:: 2.1
Reporting details about a failing assertion is achieved either by rewriting Reporting details about a failing assertion is achieved by rewriting assert
assert statements before they are run or re-evaluating the assert expression and statements before they are run. Rewritten assert statements put introspection
recording the intermediate values. Which technique is used depends on the information into the assertion failure message. ``pytest`` only rewrites test
location of the assert, ``pytest`` configuration, and Python version being used modules directly discovered by its test collection process, so asserts in
to run ``pytest``. supporting modules which are not themselves test modules will not be rewritten.
By default, ``pytest`` rewrites assert statements in test modules.
Rewritten assert statements put introspection information into the assertion failure message.
``pytest`` only rewrites test modules directly discovered by its test collection process, so
asserts in supporting modules which are not themselves test modules will not be
rewritten.
.. note:: .. note::
``pytest`` rewrites test modules on import. It does this by using an import ``pytest`` rewrites test modules on import. It does this by using an import
hook to write a new pyc files. Most of the time this works transparently. hook to write new pyc files. Most of the time this works transparently.
However, if you are messing with import yourself, the import hook may However, if you are messing with import yourself, the import hook may
interfere. If this is the case, simply use ``--assert=reinterp`` or interfere. If this is the case, use ``--assert=plain``. Additionally,
``--assert=plain``. Additionally, rewriting will fail silently if it cannot rewriting will fail silently if it cannot write new pycs, i.e. in a read-only
write new pycs, i.e. in a read-only filesystem or a zipfile. filesystem or a zipfile.
If an assert statement has not been rewritten or the Python version is less than
2.6, ``pytest`` falls back on assert reinterpretation. In assert
reinterpretation, ``pytest`` walks the frame of the function containing the
assert statement to discover sub-expression results of the failing assert
statement. You can force ``pytest`` to always use assertion reinterpretation by
passing the ``--assert=reinterp`` option.
Assert reinterpretation has a caveat not present with assert rewriting: If
evaluating the assert expression has side effects you may get a warning that the
intermediate values could not be determined safely. A common example of this
issue is an assertion which reads from a file::
assert f.read() != '...'
If this assertion fails then the re-evaluation will probably succeed!
This is because ``f.read()`` will return an empty string when it is
called the second time during the re-evaluation. However, it is
easy to rewrite the assertion and avoid any trouble::
content = f.read()
assert content != '...'
All assert introspection can be turned off by passing ``--assert=plain``.
For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_. For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting <http://pybites.blogspot.com/2011/07/behind-scenes-of-pytests-new-assertion.html>`_.
@ -318,3 +288,4 @@ For further information, Benjamin Peterson wrote up `Behind the scenes of pytest
.. versionchanged:: 3.0 .. versionchanged:: 3.0
Removes the ``--no-assert`` and``--nomagic`` options. Removes the ``--no-assert`` and``--nomagic`` options.
Removes the ``--assert=reinterp`` option.

View File

@ -80,7 +80,7 @@ If you then run it with ``--lf``::
$ pytest --lf $ pytest --lf
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
run-last-failure: rerun last 2 failures run-last-failure: rerun last 2 failures
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 50 items collected 50 items
@ -122,7 +122,7 @@ of ``FF`` and dots)::
$ pytest --ff $ pytest --ff
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
run-last-failure: rerun last 2 failures first run-last-failure: rerun last 2 failures first
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 50 items collected 50 items
@ -227,7 +227,7 @@ You can always peek at the content of the cache using the
$ py.test --cache-show $ py.test --cache-show
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
cachedir: $REGENDOC_TMPDIR/.cache cachedir: $REGENDOC_TMPDIR/.cache
------------------------------- cache values ------------------------------- ------------------------------- cache values -------------------------------
@ -246,7 +246,7 @@ by adding the ``--cache-clear`` option like this::
pytest --cache-clear pytest --cache-clear
This is recommended for invocations from Continous Integration This is recommended for invocations from Continuous Integration
servers where isolation and correctness is more important servers where isolation and correctness is more important
than speed. than speed.

View File

@ -64,7 +64,7 @@ of the failing function and hide the other one::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items collected 2 items

View File

@ -303,7 +303,7 @@ texinfo_documents = [
('Holger Krekel@*Benjamin Peterson@*Ronny Pfannschmidt@*' ('Holger Krekel@*Benjamin Peterson@*Ronny Pfannschmidt@*'
'Floris Bruynooghe@*others'), 'Floris Bruynooghe@*others'),
'pytest', 'pytest',
'simple powerful testing with Pytho', 'simple powerful testing with Python',
'Programming', 'Programming',
1), 1),
] ]

View File

@ -158,7 +158,7 @@ Builtin configuration file options
[seq] matches any character in seq [seq] matches any character in seq
[!seq] matches any char not in seq [!seq] matches any char not in seq
Default patterns are ``'.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg'``. Default patterns are ``'.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg', 'venv'``.
Setting a ``norecursedirs`` replaces the default. Here is an example of Setting a ``norecursedirs`` replaces the default. Here is an example of
how to avoid certain directories: how to avoid certain directories:

View File

@ -11,6 +11,19 @@ can change the pattern by issuing::
on the command line. Since version ``2.9``, ``--doctest-glob`` on the command line. Since version ``2.9``, ``--doctest-glob``
can be given multiple times in the command-line. can be given multiple times in the command-line.
.. versionadded:: 3.1
You can specify the encoding that will be used for those doctest files
using the ``doctest_encoding`` ini option:
.. code-block:: ini
# content of pytest.ini
[pytest]
doctest_encoding = latin1
The default encoding is UTF-8.
You can also trigger running of doctests You can also trigger running of doctests
from docstrings in all python modules (including regular from docstrings in all python modules (including regular
python test modules):: python test modules)::
@ -49,12 +62,12 @@ then you can just invoke ``pytest`` without command line options::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 1 items collected 1 items
mymodule.py . mymodule.py .
======= 1 passed in 0.12 seconds ======== ======= 1 passed in 0.12 seconds ========
It is possible to use fixtures using the ``getfixture`` helper:: It is possible to use fixtures using the ``getfixture`` helper::

View File

@ -128,7 +128,7 @@ def test_attribute_multiple():
def globf(x): def globf(x):
return x+1 return x+1
class TestRaises: class TestRaises(object):
def test_raises(self): def test_raises(self):
s = 'qwe' s = 'qwe'
raises(TypeError, "int(s)") raises(TypeError, "int(s)")
@ -167,7 +167,7 @@ def test_dynamic_compile_shows_nicely():
class TestMoreErrors: class TestMoreErrors(object):
def test_complex_error(self): def test_complex_error(self):
def f(): def f():
return 44 return 44
@ -213,23 +213,23 @@ class TestMoreErrors:
x = 0 x = 0
class TestCustomAssertMsg: class TestCustomAssertMsg(object):
def test_single_line(self): def test_single_line(self):
class A: class A(object):
a = 1 a = 1
b = 2 b = 2
assert A.a == b, "A.a appears not to be b" assert A.a == b, "A.a appears not to be b"
def test_multiline(self): def test_multiline(self):
class A: class A(object):
a = 1 a = 1
b = 2 b = 2
assert A.a == b, "A.a appears not to be b\n" \ assert A.a == b, "A.a appears not to be b\n" \
"or does not appear to be b\none of those" "or does not appear to be b\none of those"
def test_custom_repr(self): def test_custom_repr(self):
class JSON: class JSON(object):
a = 1 a = 1
def __repr__(self): def __repr__(self):
return "This is JSON\n{\n 'foo': 'bar'\n}" return "This is JSON\n{\n 'foo': 'bar'\n}"

View File

@ -1,7 +1,7 @@
def setup_module(module): def setup_module(module):
module.TestStateFullThing.classcount = 0 module.TestStateFullThing.classcount = 0
class TestStateFullThing: class TestStateFullThing(object):
def setup_class(cls): def setup_class(cls):
cls.classcount += 1 cls.classcount += 1

View File

@ -15,7 +15,7 @@ example: specifying and selecting acceptance tests
def pytest_funcarg__accept(request): def pytest_funcarg__accept(request):
return AcceptFixture(request) return AcceptFixture(request)
class AcceptFixture: class AcceptFixture(object):
def __init__(self, request): def __init__(self, request):
if not request.config.option.acceptance: if not request.config.option.acceptance:
pytest.skip("specify -A to run acceptance tests") pytest.skip("specify -A to run acceptance tests")
@ -61,7 +61,7 @@ extend the `accept example`_ by putting this in our test module:
arg.tmpdir.mkdir("special") arg.tmpdir.mkdir("special")
return arg return arg
class TestSpecialAcceptance: class TestSpecialAcceptance(object):
def test_sometest(self, accept): def test_sometest(self, accept):
assert accept.tmpdir.join("special").check() assert accept.tmpdir.join("special").check()

View File

@ -7,7 +7,7 @@ def setup(request):
yield setup yield setup
setup.finalize() setup.finalize()
class CostlySetup: class CostlySetup(object):
def __init__(self): def __init__(self):
import time import time
print ("performing costly setup") print ("performing costly setup")

View File

@ -21,7 +21,7 @@ You can "mark" a test function with custom metadata like this::
pass pass
def test_another(): def test_another():
pass pass
class TestClass: class TestClass(object):
def test_method(self): def test_method(self):
pass pass
@ -31,7 +31,7 @@ You can then restrict a test run to only run tests marked with ``webtest``::
$ pytest -v -m webtest $ pytest -v -m webtest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items collecting ... collected 4 items
@ -45,7 +45,7 @@ Or the inverse, running all tests except the webtest ones::
$ pytest -v -m "not webtest" $ pytest -v -m "not webtest"
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items collecting ... collected 4 items
@ -66,7 +66,7 @@ tests based on their module, class, method, or function name::
$ pytest -v test_server.py::TestClass::test_method $ pytest -v test_server.py::TestClass::test_method
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 5 items collecting ... collected 5 items
@ -79,7 +79,7 @@ You can also select on the class::
$ pytest -v test_server.py::TestClass $ pytest -v test_server.py::TestClass
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items collecting ... collected 4 items
@ -92,7 +92,7 @@ Or select multiple nodes::
$ pytest -v test_server.py::TestClass test_server.py::test_send_http $ pytest -v test_server.py::TestClass test_server.py::test_send_http
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 8 items collecting ... collected 8 items
@ -130,7 +130,7 @@ select tests based on their names::
$ pytest -v -k http # running with the above defined example module $ pytest -v -k http # running with the above defined example module
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items collecting ... collected 4 items
@ -144,7 +144,7 @@ And you can also run all tests except the ones that match the keyword::
$ pytest -k "not send_http" -v $ pytest -k "not send_http" -v
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items collecting ... collected 4 items
@ -160,7 +160,7 @@ Or to select "http" and "quick" tests::
$ pytest -k "http or quick" -v $ pytest -k "http or quick" -v
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items collecting ... collected 4 items
@ -205,7 +205,7 @@ You can ask which markers exist for your test suite - the list includes our just
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html @pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html @pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples. @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@ -242,7 +242,7 @@ its test methods::
# content of test_mark_classlevel.py # content of test_mark_classlevel.py
import pytest import pytest
@pytest.mark.webtest @pytest.mark.webtest
class TestClass: class TestClass(object):
def test_startup(self): def test_startup(self):
pass pass
def test_startup_and_more(self): def test_startup_and_more(self):
@ -256,14 +256,14 @@ To remain backward-compatible with Python 2.4 you can also set a
import pytest import pytest
class TestClass: class TestClass(object):
pytestmark = pytest.mark.webtest pytestmark = pytest.mark.webtest
or if you need to use multiple markers you can use a list:: or if you need to use multiple markers you can use a list::
import pytest import pytest
class TestClass: class TestClass(object):
pytestmark = [pytest.mark.webtest, pytest.mark.slowtest] pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]
You can also set a module level marker:: You can also set a module level marker::
@ -352,7 +352,7 @@ the test needs::
$ pytest -E stage2 $ pytest -E stage2
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items
@ -364,7 +364,7 @@ and here is one that specifies exactly the environment needed::
$ pytest -E stage1 $ pytest -E stage1
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items
@ -381,7 +381,7 @@ The ``--markers`` option always gives you a list of available markers::
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html @pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html @pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples. @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@ -407,7 +407,7 @@ code you can read over all such settings. Example::
pytestmark = pytest.mark.glob("module", x=1) pytestmark = pytest.mark.glob("module", x=1)
@pytest.mark.glob("class", x=2) @pytest.mark.glob("class", x=2)
class TestClass: class TestClass(object):
@pytest.mark.glob("function", x=3) @pytest.mark.glob("function", x=3)
def test_something(self): def test_something(self):
pass pass
@ -450,7 +450,7 @@ for your particular platform, you could use the following plugin::
import sys import sys
import pytest import pytest
ALL = set("darwin linux2 win32".split()) ALL = set("darwin linux win32".split())
def pytest_runtest_setup(item): def pytest_runtest_setup(item):
if isinstance(item, item.Function): if isinstance(item, item.Function):
@ -470,7 +470,7 @@ Let's do a little test file to show how this looks like::
def test_if_apple_is_evil(): def test_if_apple_is_evil():
pass pass
@pytest.mark.linux2 @pytest.mark.linux
def test_if_linux_works(): def test_if_linux_works():
pass pass
@ -481,32 +481,32 @@ Let's do a little test file to show how this looks like::
def test_runs_everywhere(): def test_runs_everywhere():
pass pass
then you will see two test skipped and two executed tests as expected:: then you will see two tests skipped and two executed tests as expected::
$ pytest -rs # this option reports skip reasons $ pytest -rs # this option reports skip reasons
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items collected 4 items
test_plat.py sss. test_plat.py s.s.
======= short test summary info ======== ======= short test summary info ========
SKIP [3] $REGENDOC_TMPDIR/conftest.py:12: cannot run on platform linux SKIP [2] $REGENDOC_TMPDIR/conftest.py:12: cannot run on platform linux
======= 1 passed, 3 skipped in 0.12 seconds ======== ======= 2 passed, 2 skipped in 0.12 seconds ========
Note that if you specify a platform via the marker-command line option like this:: Note that if you specify a platform via the marker-command line option like this::
$ pytest -m linux2 $ pytest -m linux
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items collected 4 items
test_plat.py s test_plat.py .
======= 3 tests deselected ======== ======= 3 tests deselected ========
======= 1 skipped, 3 deselected in 0.12 seconds ======== ======= 1 passed, 3 deselected in 0.12 seconds ========
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests. then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
@ -551,7 +551,7 @@ We can now use the ``-m option`` to select one set::
$ pytest -m interface --tb=short $ pytest -m interface --tb=short
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items collected 4 items
@ -573,7 +573,7 @@ or to select both "event" and "interface" tests::
$ pytest -m "interface or event" --tb=short $ pytest -m "interface or event" --tb=short
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items collected 4 items

View File

@ -16,7 +16,7 @@ def python1(request, tmpdir):
def python2(request, python1): def python2(request, python1):
return Python(request.param, python1.picklefile) return Python(request.param, python1.picklefile)
class Python: class Python(object):
def __init__(self, version, picklefile): def __init__(self, version, picklefile):
self.pythonpath = py.path.local.sysfind(version) self.pythonpath = py.path.local.sysfind(version)
if not self.pythonpath: if not self.pythonpath:

View File

@ -27,7 +27,7 @@ now execute the test specification::
nonpython $ pytest test_simple.yml nonpython $ pytest test_simple.yml
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR/nonpython, inifile: rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collected 2 items collected 2 items
@ -59,7 +59,7 @@ consulted when reporting in ``verbose`` mode::
nonpython $ pytest -v nonpython $ pytest -v
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR/nonpython, inifile: rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collecting ... collected 2 items collecting ... collected 2 items
@ -81,7 +81,7 @@ interesting to just look at the collection tree::
nonpython $ pytest --collect-only nonpython $ pytest --collect-only
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR/nonpython, inifile: rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collected 2 items collected 2 items
<YamlFile 'test_simple.yml'> <YamlFile 'test_simple.yml'>

View File

@ -130,7 +130,7 @@ objects, they are still using the default pytest representation::
$ pytest test_time.py --collect-only $ pytest test_time.py --collect-only
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 6 items collected 6 items
<Module 'test_time.py'> <Module 'test_time.py'>
@ -168,7 +168,7 @@ only have to work a bit to construct the correct arguments for pytest's
scenario1 = ('basic', {'attribute': 'value'}) scenario1 = ('basic', {'attribute': 'value'})
scenario2 = ('advanced', {'attribute': 'value2'}) scenario2 = ('advanced', {'attribute': 'value2'})
class TestSampleWithScenarios: class TestSampleWithScenarios(object):
scenarios = [scenario1, scenario2] scenarios = [scenario1, scenario2]
def test_demo1(self, attribute): def test_demo1(self, attribute):
@ -181,7 +181,7 @@ this is a fully self-contained example which you can run with::
$ pytest test_scenarios.py $ pytest test_scenarios.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items collected 4 items
@ -194,7 +194,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
$ pytest --collect-only test_scenarios.py $ pytest --collect-only test_scenarios.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items collected 4 items
<Module 'test_scenarios.py'> <Module 'test_scenarios.py'>
@ -241,9 +241,9 @@ creates a database object for the actual test invocations::
if 'db' in metafunc.fixturenames: if 'db' in metafunc.fixturenames:
metafunc.parametrize("db", ['d1', 'd2'], indirect=True) metafunc.parametrize("db", ['d1', 'd2'], indirect=True)
class DB1: class DB1(object):
"one database object" "one database object"
class DB2: class DB2(object):
"alternative database object" "alternative database object"
@pytest.fixture @pytest.fixture
@ -259,7 +259,7 @@ Let's first see how it looks like at collection time::
$ pytest test_backends.py --collect-only $ pytest test_backends.py --collect-only
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items collected 2 items
<Module 'test_backends.py'> <Module 'test_backends.py'>
@ -320,7 +320,7 @@ The result of this test will be successful::
$ pytest test_indirect_list.py --collect-only $ pytest test_indirect_list.py --collect-only
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items
<Module 'test_indirect_list.py'> <Module 'test_indirect_list.py'>
@ -350,7 +350,7 @@ parametrizer`_ but in a lot less code::
metafunc.parametrize(argnames, [[funcargs[name] for name in argnames] metafunc.parametrize(argnames, [[funcargs[name] for name in argnames]
for funcargs in funcarglist]) for funcargs in funcarglist])
class TestClass: class TestClass(object):
# a map specifying multiple argument sets for a test method # a map specifying multiple argument sets for a test method
params = { params = {
'test_equals': [dict(a=1, b=2), dict(a=3, b=3), ], 'test_equals': [dict(a=1, b=2), dict(a=3, b=3), ],
@ -397,10 +397,12 @@ is to be run with different sets of arguments for its three arguments:
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize):: Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
. $ pytest -rs -q multipython.py . $ pytest -rs -q multipython.py
sssssssssssssss.........sss.........sss......... sssssssssssssssssssssssssssssssssssssssssssss...
======= short test summary info ======== ======= short test summary info ========
SKIP [21] $REGENDOC_TMPDIR/CWD/multipython.py:23: 'python2.6' not found SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:23: 'python2.6' not found
27 passed, 21 skipped in 0.12 seconds SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:23: 'python3.4' not found
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:23: 'python2.7' not found
3 passed, 45 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports Indirect parametrization of optional implementations/imports
-------------------------------------------------------------------- --------------------------------------------------------------------
@ -447,7 +449,7 @@ If you run this with reporting for skips enabled::
$ pytest -rs test_module.py $ pytest -rs test_module.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items collected 2 items

View File

@ -4,7 +4,7 @@
def test_function(): def test_function():
pass pass
class TestClass: class TestClass(object):
def test_method(self): def test_method(self):
pass pass
def test_anothermethod(self): def test_anothermethod(self):

View File

@ -95,7 +95,7 @@ the :confval:`python_files`, :confval:`python_classes` and
:confval:`python_functions` configuration options. Example:: :confval:`python_functions` configuration options. Example::
# content of pytest.ini # content of pytest.ini
# can also be defined in in tox.ini or setup.cfg file, although the section # can also be defined in tox.ini or setup.cfg file, although the section
# name in setup.cfg files should be "tool:pytest" # name in setup.cfg files should be "tool:pytest"
[pytest] [pytest]
python_files=check_*.py python_files=check_*.py
@ -107,7 +107,7 @@ This would make ``pytest`` look for tests in files that match the ``check_*
that match ``*_check``. For example, if we have:: that match ``*_check``. For example, if we have::
# content of check_myapp.py # content of check_myapp.py
class CheckMyApp: class CheckMyApp(object):
def simple_check(self): def simple_check(self):
pass pass
def complex_check(self): def complex_check(self):
@ -117,7 +117,7 @@ then the test collection looks like this::
$ pytest --collect-only $ pytest --collect-only
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 2 items collected 2 items
<Module 'check_myapp.py'> <Module 'check_myapp.py'>
@ -163,7 +163,7 @@ You can always peek at the collection tree without running tests like this::
. $ pytest --collect-only pythoncollection.py . $ pytest --collect-only pythoncollection.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 3 items collected 3 items
<Module 'CWD/pythoncollection.py'> <Module 'CWD/pythoncollection.py'>
@ -230,7 +230,7 @@ will be left out::
$ pytest --collect-only $ pytest --collect-only
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 0 items collected 0 items

View File

@ -11,7 +11,7 @@ get on the terminal - we are working on that)::
assertion $ pytest failure_demo.py assertion $ pytest failure_demo.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR/assertion, inifile: rootdir: $REGENDOC_TMPDIR/assertion, inifile:
collected 42 items collected 42 items
@ -359,7 +359,7 @@ get on the terminal - we are working on that)::
> int(s) > int(s)
E ValueError: invalid literal for int() with base 10: 'qwe' E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1204>:1: ValueError <0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1207>:1: ValueError
_______ TestRaises.test_raises_doesnt ________ _______ TestRaises.test_raises_doesnt ________
self = <failure_demo.TestRaises object at 0xdeadbeef> self = <failure_demo.TestRaises object at 0xdeadbeef>
@ -550,7 +550,7 @@ get on the terminal - we are working on that)::
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef> self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
def test_single_line(self): def test_single_line(self):
class A: class A(object):
a = 1 a = 1
b = 2 b = 2
> assert A.a == b, "A.a appears not to be b" > assert A.a == b, "A.a appears not to be b"
@ -564,7 +564,7 @@ get on the terminal - we are working on that)::
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef> self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
def test_multiline(self): def test_multiline(self):
class A: class A(object):
a = 1 a = 1
b = 2 b = 2
> assert A.a == b, "A.a appears not to be b\n" \ > assert A.a == b, "A.a appears not to be b\n" \
@ -581,7 +581,7 @@ get on the terminal - we are working on that)::
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef> self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
def test_custom_repr(self): def test_custom_repr(self):
class JSON: class JSON(object):
a = 1 a = 1
def __repr__(self): def __repr__(self):
return "This is JSON\n{\n 'foo': 'bar'\n}" return "This is JSON\n{\n 'foo': 'bar'\n}"

View File

@ -113,7 +113,7 @@ directory with the above conftest.py::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items collected 0 items
@ -164,7 +164,7 @@ and when running it will see a skipped "slow" test::
$ pytest -rs # "-rs" means report details on the little 's' $ pytest -rs # "-rs" means report details on the little 's'
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items collected 2 items
@ -178,7 +178,7 @@ Or run it including the ``slow`` marked test::
$ pytest --runslow $ pytest --runslow
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items collected 2 items
@ -302,7 +302,7 @@ which will add the string to the test header accordingly::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
project deps: mylib-1.1 project deps: mylib-1.1
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items collected 0 items
@ -327,7 +327,7 @@ which will add info only when run with "--v"::
$ pytest -v $ pytest -v
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
info1: did you know that ... info1: did you know that ...
did you? did you?
@ -340,7 +340,7 @@ and nothing when run plainly::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items collected 0 items
@ -374,7 +374,7 @@ Now we can profile which test functions execute the slowest::
$ pytest --durations=3 $ pytest --durations=3
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items collected 3 items
@ -425,7 +425,7 @@ tests in a class. Here is a test module example:
import pytest import pytest
@pytest.mark.incremental @pytest.mark.incremental
class TestUserHandling: class TestUserHandling(object):
def test_login(self): def test_login(self):
pass pass
def test_modification(self): def test_modification(self):
@ -440,7 +440,7 @@ If we run this::
$ pytest -rx $ pytest -rx
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items collected 4 items
@ -476,14 +476,14 @@ concept. It's however recommended to have explicit fixture references in your
tests or test classes rather than relying on implicitly executing tests or test classes rather than relying on implicitly executing
setup/teardown functions, especially if they are far away from the actual tests. setup/teardown functions, especially if they are far away from the actual tests.
Here is a an example for making a ``db`` fixture available in a directory: Here is an example for making a ``db`` fixture available in a directory:
.. code-block:: python .. code-block:: python
# content of a/conftest.py # content of a/conftest.py
import pytest import pytest
class DB: class DB(object):
pass pass
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
@ -519,7 +519,7 @@ We can run this::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 7 items collected 7 items
@ -585,7 +585,7 @@ environment you can implement a hook that gets called when the test
"report" object is about to be created. Here we write out all failing "report" object is about to be created. Here we write out all failing
test calls and also access a fixture (if it was used by the test) in test calls and also access a fixture (if it was used by the test) in
case you want to query/look at it during your post processing. In our case you want to query/look at it during your post processing. In our
case we just write some informations out to a ``failures`` file: case we just write some information out to a ``failures`` file:
.. code-block:: python .. code-block:: python
@ -627,7 +627,7 @@ and run them::
$ pytest test_module.py $ pytest test_module.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items collected 2 items
@ -678,7 +678,7 @@ here is a little example implemented via a local plugin:
outcome = yield outcome = yield
rep = outcome.get_result() rep = outcome.get_result()
# set an report attribute for each phase of a call, which can # set a report attribute for each phase of a call, which can
# be "setup", "call", "teardown" # be "setup", "call", "teardown"
setattr(item, "rep_" + rep.when, rep) setattr(item, "rep_" + rep.when, rep)
@ -721,7 +721,7 @@ and run it::
$ pytest -s test_module.py $ pytest -s test_module.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items collected 3 items

View File

@ -28,7 +28,7 @@ will be called ahead of running any tests::
# content of test_module.py # content of test_module.py
class TestHello: class TestHello(object):
@classmethod @classmethod
def callme(cls): def callme(cls):
print ("callme called!") print ("callme called!")
@ -39,7 +39,7 @@ will be called ahead of running any tests::
def test_method2(self): def test_method2(self):
print ("test_method1 called") print ("test_method1 called")
class TestOther: class TestOther(object):
@classmethod @classmethod
def callme(cls): def callme(cls):
print ("callme other called") print ("callme other called")

View File

@ -66,14 +66,6 @@ This completely avoids previous issues of confusing assertion-reporting.
It also means, that you can use Python's ``-O`` optimization without losing It also means, that you can use Python's ``-O`` optimization without losing
assertions in test modules. assertions in test modules.
``pytest`` contains a second, mostly obsolete, assert debugging technique
invoked via ``--assert=reinterpret``: When an ``assert`` statement fails, ``pytest`` re-interprets
the expression part to show intermediate values. This technique suffers
from a caveat that the rewriting does not: If your expression has side
effects (better to avoid them anyway!) the intermediate values may not
be the same, confusing the reinterpreter and obfuscating the initial
error (this is also explained at the command line if it happens).
You can also turn off all assertion interaction using the You can also turn off all assertion interaction using the
``--assert=plain`` option. ``--assert=plain`` option.

View File

@ -11,7 +11,7 @@ pytest fixtures: explicit, modular, scalable
.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit .. _`xUnit`: http://en.wikipedia.org/wiki/XUnit
.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software .. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software
.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition .. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection
The `purpose of test fixtures`_ is to provide a fixed baseline The `purpose of test fixtures`_ is to provide a fixed baseline
upon which tests can reliably and repeatedly execute. pytest fixtures upon which tests can reliably and repeatedly execute. pytest fixtures
@ -70,7 +70,7 @@ marked ``smtp`` fixture function. Running the test looks like this::
$ pytest test_smtpsimple.py $ pytest test_smtpsimple.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items
@ -188,7 +188,7 @@ inspect what is going on and can now run the tests::
$ pytest test_module.py $ pytest test_module.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items collected 2 items
@ -257,8 +257,9 @@ the code after the *yield* statement serves as the teardown code.::
print("teardown smtp") print("teardown smtp")
smtp.close() smtp.close()
The ``print`` and ``smtp.close()`` statements will execute when the last test using The ``print`` and ``smtp.close()`` statements will execute when the last test in
the fixture in the module has finished execution, regardless of the exception status of the tests. the module has finished execution, regardless of the exception status of the
tests.
Let's execute it:: Let's execute it::
@ -318,8 +319,7 @@ the ``with`` statement ends.
request.addfinalizer(fin) request.addfinalizer(fin)
return smtp # provide the fixture value return smtp # provide the fixture value
The ``fin`` function will execute when the last test using The ``fin`` function will execute when the last test in the module has finished execution.
the fixture in the module has finished execution.
This method is still fully supported, but ``yield`` is recommended from 2.10 onward because This method is still fully supported, but ``yield`` is recommended from 2.10 onward because
it is considered simpler and better describes the natural code flow. it is considered simpler and better describes the natural code flow.
@ -520,7 +520,7 @@ Running the above tests results in the following test IDs being used::
$ pytest --collect-only $ pytest --collect-only
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 11 items collected 11 items
<Module 'test_anothersmtp.py'> <Module 'test_anothersmtp.py'>
@ -557,7 +557,7 @@ and instantiate an object ``app`` where we stick the already defined
import pytest import pytest
class App: class App(object):
def __init__(self, smtp): def __init__(self, smtp):
self.smtp = smtp self.smtp = smtp
@ -573,7 +573,7 @@ Here we declare an ``app`` fixture which receives the previously defined
$ pytest -v test_appsetup.py $ pytest -v test_appsetup.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 2 items collecting ... collected 2 items
@ -642,7 +642,7 @@ Let's run the tests in verbose mode and with looking at the print-output::
$ pytest -v -s test_module.py $ pytest -v -s test_module.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5m
cachedir: .cache cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 8 items collecting ... collected 8 items
@ -728,7 +728,7 @@ and declare its use in a test module via a ``usefixtures`` marker::
import pytest import pytest
@pytest.mark.usefixtures("cleandir") @pytest.mark.usefixtures("cleandir")
class TestDirectoryInit: class TestDirectoryInit(object):
def test_cwd_starts_empty(self): def test_cwd_starts_empty(self):
assert os.listdir(os.getcwd()) == [] assert os.listdir(os.getcwd()) == []
with open("myfile", "w") as f: with open("myfile", "w") as f:
@ -791,7 +791,7 @@ self-contained implementation of this idea::
import pytest import pytest
class DB: class DB(object):
def __init__(self): def __init__(self):
self.intransaction = [] self.intransaction = []
def begin(self, name): def begin(self, name):
@ -803,7 +803,7 @@ self-contained implementation of this idea::
def db(): def db():
return DB() return DB()
class TestClass: class TestClass(object):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def transact(self, request, db): def transact(self, request, db):
db.begin(request.function.__name__) db.begin(request.function.__name__)
@ -861,7 +861,7 @@ into a conftest.py file **without** using ``autouse``::
and then e.g. have a TestClass using it by declaring the need:: and then e.g. have a TestClass using it by declaring the need::
@pytest.mark.usefixtures("transact") @pytest.mark.usefixtures("transact")
class TestClass: class TestClass(object):
def test_method1(self): def test_method1(self):
... ...
@ -1002,7 +1002,7 @@ Given the tests file structure is:
@pytest.mark.parametrize('username', ['directly-overridden-username-other']) @pytest.mark.parametrize('username', ['directly-overridden-username-other'])
def test_username_other(other_username): def test_username_other(other_username):
assert username == 'other-directly-overridden-username-other' assert other_username == 'other-directly-overridden-username-other'
In the example above, a fixture value is overridden by the test parameter value. Note that the value of the fixture In the example above, a fixture value is overridden by the test parameter value. Note that the value of the fixture
can be overridden this way even if the test doesn't use it directly (doesn't mention it in the function prototype). can be overridden this way even if the test doesn't use it directly (doesn't mention it in the function prototype).

View File

@ -24,7 +24,7 @@ resources. Here is a basic example how we could implement
a per-session Database object:: a per-session Database object::
# content of conftest.py # content of conftest.py
class Database: class Database(object):
def __init__(self): def __init__(self):
print ("database instance created") print ("database instance created")
def destroy(self): def destroy(self):
@ -97,7 +97,7 @@ sets. pytest-2.3 introduces a decorator for use on the factory itself::
... # use request.param ... # use request.param
Here the factory will be invoked twice (with the respective "mysql" Here the factory will be invoked twice (with the respective "mysql"
and "pg" values set as ``request.param`` attributes) and and all of and "pg" values set as ``request.param`` attributes) and all of
the tests requiring "db" will run twice as well. The "mysql" and the tests requiring "db" will run twice as well. The "mysql" and
"pg" values will also be used for reporting the test-invocation variants. "pg" values will also be used for reporting the test-invocation variants.

View File

@ -1,7 +1,7 @@
import textwrap import textwrap
import inspect import inspect
class Writer: class Writer(object):
def __init__(self, clsname): def __init__(self, clsname):
self.clsname = clsname self.clsname = clsname

View File

@ -26,7 +26,7 @@ Installation::
To check your installation has installed the correct version:: To check your installation has installed the correct version::
$ pytest --version $ pytest --version
This is pytest version 3.0.4, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py This is pytest version 3.0.6, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
.. _`simpletest`: .. _`simpletest`:
@ -46,7 +46,7 @@ That's it. You can execute the test function now::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items
@ -111,7 +111,7 @@ to group tests logically, in classes and modules. Let's write a class
containing two tests:: containing two tests::
# content of test_class.py # content of test_class.py
class TestClass: class TestClass(object):
def test_one(self): def test_one(self):
x = "this" x = "this"
assert 'h' in x assert 'h' in x

View File

@ -16,10 +16,12 @@ Conventions for Python test discovery
* If no arguments are specified then collection starts from :confval:`testpaths` * If no arguments are specified then collection starts from :confval:`testpaths`
(if configured) or the current directory. Alternatively, command line arguments (if configured) or the current directory. Alternatively, command line arguments
can be used in any combination of directories, file names or node ids. can be used in any combination of directories, file names or node ids.
* recurse into directories, unless they match :confval:`norecursedirs` * Recurse into directories, unless they match :confval:`norecursedirs`.
* ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_. * In those directories, search for ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
* ``Test`` prefixed test classes (without an ``__init__`` method) * From those files, collect test items:
* ``test_`` prefixed test functions or methods are test items
* ``test_`` prefixed test functions or methods outside of class
* ``test_`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
For examples of how to customize your test discovery :doc:`example/pythoncollection`. For examples of how to customize your test discovery :doc:`example/pythoncollection`.
@ -125,7 +127,7 @@ required configurations.
The reason for this somewhat evolved importing technique is The reason for this somewhat evolved importing technique is
that in larger projects multiple test modules might import that in larger projects multiple test modules might import
from each other and thus deriving a canonical import name helps from each other and thus deriving a canonical import name helps
to avoid surprises such as a test modules getting imported twice. to avoid surprises such as a test module getting imported twice.
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv .. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
@ -145,7 +147,7 @@ dependencies are isolated from the system Python installation.
If you frequently release code and want to make sure that your actual If you frequently release code and want to make sure that your actual
package passes all tests you may want to look into `tox`_, the package passes all tests you may want to look into `tox`_, the
virtualenv test automation tool and its `pytest support virtualenv test automation tool and its `pytest support
<http://testrun.org/tox/latest/example/pytest.html>`_. <https://tox.readthedocs.io/en/latest/example/pytest.html>`_.
Tox helps you to setup virtualenv environments with pre-defined Tox helps you to setup virtualenv environments with pre-defined
dependencies and then executing a pre-configured test command with dependencies and then executing a pre-configured test command with
options. It will run tests against the installed package and not options. It will run tests against the installed package and not

View File

@ -14,18 +14,18 @@ An example of a simple test:
.. code-block:: python .. code-block:: python
# content of test_sample.py # content of test_sample.py
def func(x): def inc(x):
return x + 1 return x + 1
def test_answer(): def test_answer():
assert func(3) == 5 assert inc(3) == 5
To execute it:: To execute it::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items
@ -35,9 +35,9 @@ To execute it::
_______ test_answer ________ _______ test_answer ________
def test_answer(): def test_answer():
> assert func(3) == 5 > assert inc(3) == 5
E assert 4 == 5 E assert 4 == 5
E + where 4 = func(3) E + where 4 = inc(3)
test_sample.py:5: AssertionError test_sample.py:5: AssertionError
======= 1 failed in 0.12 seconds ======== ======= 1 failed in 0.12 seconds ========

View File

@ -35,7 +35,7 @@ patch this function before calling into a function which uses it::
assert x == '/abc/.ssh' assert x == '/abc/.ssh'
Here our test function monkeypatches ``os.path.expanduser`` and Here our test function monkeypatches ``os.path.expanduser`` and
then calls into an function that calls it. After the test function then calls into a function that calls it. After the test function
finishes the ``os.path.expanduser`` modification will be undone. finishes the ``os.path.expanduser`` modification will be undone.
example: preventing "requests" from remote operations example: preventing "requests" from remote operations
@ -60,7 +60,7 @@ so that any attempts within tests to create http requests will fail.
Be advised that it is not recommended to patch builtin functions such as ``open``, Be advised that it is not recommended to patch builtin functions such as ``open``,
``compile``, etc., because it might break pytest's internals. If that's ``compile``, etc., because it might break pytest's internals. If that's
unavoidable, passing ``--tb=native``, ``--assert=plain`` and ``--capture=no`` might unavoidable, passing ``--tb=native``, ``--assert=plain`` and ``--capture=no`` might
help althought there's no guarantee. help although there's no guarantee.
Method reference of the monkeypatch fixture Method reference of the monkeypatch fixture

View File

@ -55,7 +55,7 @@ them in turn::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items collected 3 items
@ -103,7 +103,7 @@ Let's run this::
$ pytest $ pytest
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items collected 3 items

View File

@ -58,7 +58,7 @@ Here are some examples of projects using ``pytest`` (please send notes via :ref:
* `katcp <https://bitbucket.org/hodgestar/katcp>`_ Telescope communication protocol over Twisted * `katcp <https://bitbucket.org/hodgestar/katcp>`_ Telescope communication protocol over Twisted
* `kss plugin timer <http://pypi.python.org/pypi/kss.plugin.timer>`_ * `kss plugin timer <http://pypi.python.org/pypi/kss.plugin.timer>`_
* `pyudev <https://pyudev.readthedocs.io/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev * `pyudev <https://pyudev.readthedocs.io/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev
* `pytest-localserver <https://bitbucket.org/basti/pytest-localserver/>`_ a plugin for pytest that provides a httpserver and smtpserver * `pytest-localserver <https://bitbucket.org/basti/pytest-localserver/>`_ a plugin for pytest that provides an httpserver and smtpserver
* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch * `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
These projects help integrate ``pytest`` into other Python frameworks: These projects help integrate ``pytest`` into other Python frameworks:

View File

@ -1,8 +1,12 @@
.. _`asserting warnings`:
.. _assertwarnings: .. _assertwarnings:
Asserting Warnings Asserting Warnings
===================================================== =====================================================
.. _`asserting warnings with the warns function`:
.. _warns: .. _warns:
Asserting warnings with the warns function Asserting warnings with the warns function
@ -46,6 +50,8 @@ Alternatively, you can examine raised warnings in detail using the
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
differently; see :ref:`ensuring_function_triggers`. differently; see :ref:`ensuring_function_triggers`.
.. _`recording warnings`:
.. _recwarn: .. _recwarn:
Recording warnings Recording warnings
@ -99,6 +105,8 @@ class of the warning. The ``message`` is the warning itself; calling
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
differently; see :ref:`ensuring_function_triggers`. differently; see :ref:`ensuring_function_triggers`.
.. _`ensuring a function triggers a deprecation warning`:
.. _ensuring_function_triggers: .. _ensuring_function_triggers:
Ensuring a function triggers a deprecation warning Ensuring a function triggers a deprecation warning

View File

@ -2,7 +2,7 @@
.. _skipping: .. _skipping:
Skip and xfail: dealing with tests that can not succeed Skip and xfail: dealing with tests that cannot succeed
===================================================================== =====================================================================
If you have test functions that cannot be run on certain platforms If you have test functions that cannot be run on certain platforms
@ -98,7 +98,7 @@ You can use the ``skipif`` decorator (and any other marker) on classes::
@pytest.mark.skipif(sys.platform == 'win32', @pytest.mark.skipif(sys.platform == 'win32',
reason="does not run on windows") reason="does not run on windows")
class TestPosixCalls: class TestPosixCalls(object):
def test_function(self): def test_function(self):
"will not be setup or run under 'win32' platform" "will not be setup or run under 'win32' platform"
@ -224,7 +224,7 @@ Running it with the report-on-xfail option gives this output::
example $ pytest -rx xfail_demo.py example $ pytest -rx xfail_demo.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR/example, inifile: rootdir: $REGENDOC_TMPDIR/example, inifile:
collected 7 items collected 7 items

View File

@ -110,7 +110,7 @@ If you want to disable a complete test class you
can set the class-level attribute ``disabled``. can set the class-level attribute ``disabled``.
For example, in order to avoid running some tests on Win32:: For example, in order to avoid running some tests on Win32::
class TestPosixOnly: class TestPosixOnly(object):
disabled = sys.platform == 'win32' disabled = sys.platform == 'win32'
def test_xxx(self): def test_xxx(self):

View File

@ -71,7 +71,7 @@ you can ad-hoc distribute your tests by typing::
pytest -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg pytest -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg
This will synchronize your ``mypkg`` package directory This will synchronize your ``mypkg`` package directory
to an remote ssh account and then locally collect tests to a remote ssh account and then locally collect tests
and send them to remote places for execution. and send them to remote places for execution.
You can specify multiple ``--rsyncdir`` directories You can specify multiple ``--rsyncdir`` directories

View File

@ -29,7 +29,7 @@ Running this would result in a passed test except for the last
$ pytest test_tmpdir.py $ pytest test_tmpdir.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items collected 1 items

View File

@ -17,6 +17,14 @@ run using ``pytest``. We assume here that you are familiar with writing
``unittest.TestCase`` style tests and rather focus on ``unittest.TestCase`` style tests and rather focus on
integration aspects. integration aspects.
Note that this is meant as a provisional way of running your test code
until you fully convert to pytest-style tests. To fully take advantage of
:ref:`fixtures <fixture>`, :ref:`parametrization <parametrize>` and
:ref:`hooks <writing-plugins>` you should convert (tools like `unittest2pytest
<https://pypi.python.org/pypi/unittest2pytest/>`__ are helpful).
Also, not all 3rd party pluging are expected to work best with
``unittest.TestCase`` style tests.
Usage Usage
------------------------------------------------------------------- -------------------------------------------------------------------
@ -63,7 +71,7 @@ it from a unittest-style test::
@pytest.fixture(scope="class") @pytest.fixture(scope="class")
def db_class(request): def db_class(request):
class DummyDB: class DummyDB(object):
pass pass
# set a class attribute on the invoking test context # set a class attribute on the invoking test context
request.cls.db = DummyDB() request.cls.db = DummyDB()
@ -100,7 +108,7 @@ the ``self.db`` values in the traceback::
$ pytest test_unittest_db.py $ pytest test_unittest_db.py
======= test session starts ======== ======= test session starts ========
platform linux -- Python 3.5.2, pytest-3.0.4, py-1.4.31, pluggy-0.4.0 platform linux -- Python 3.5.2, pytest-3.0.6, py-1.4.33, pluggy-0.4.0
rootdir: $REGENDOC_TMPDIR, inifile: rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items collected 2 items
@ -191,12 +199,3 @@ was executed ahead of the ``test_method``.
pytest fixtures into unittest suites. And of course you can also start pytest fixtures into unittest suites. And of course you can also start
to selectively leave away the ``unittest.TestCase`` subclassing, use to selectively leave away the ``unittest.TestCase`` subclassing, use
plain asserts and get the unlimited pytest feature set. plain asserts and get the unlimited pytest feature set.
Converting from unittest to pytest
---------------------------------------
If you want to convert your unittest testcases to pytest, there are
some helpers like `unittest2pytest
<https://pypi.python.org/pypi/unittest2pytest/>`__, which uses lib2to3
and introspection for the transformation.

View File

@ -16,8 +16,8 @@ You can invoke testing through the Python interpreter from the command line::
python -m pytest [...] python -m pytest [...]
This is equivalent to invoking the command line script ``pytest [...]`` This is almost equivalent to invoking the command line script ``pytest [...]``
directly. directly, except that python will also add the current directory to ``sys.path``.
Getting help on version, option names, environment variables Getting help on version, option names, environment variables
-------------------------------------------------------------- --------------------------------------------------------------
@ -49,7 +49,7 @@ Several test run options::
# will select TestMyClass.test_something # will select TestMyClass.test_something
# but not TestMyClass.test_method_simple # but not TestMyClass.test_method_simple
pytest test_mod.py::test_func # only run tests that match the "node ID", pytest test_mod.py::test_func # only run tests that match the "node ID",
# e.g "test_mod.py::test_func" will select # e.g. "test_mod.py::test_func" will select
# only test_func in test_mod.py # only test_func in test_mod.py
pytest test_mod.py::TestClass::test_method # run a single method in pytest test_mod.py::TestClass::test_method # run a single method in
# a single class # a single class
@ -76,7 +76,7 @@ Examples for modifying traceback printing::
The ``--full-trace`` causes very long traces to be printed on error (longer The ``--full-trace`` causes very long traces to be printed on error (longer
than ``--tb=long``). It also ensures that a stack trace is printed on than ``--tb=long``). It also ensures that a stack trace is printed on
**KeyboardInterrrupt** (Ctrl+C). **KeyboardInterrupt** (Ctrl+C).
This is very useful if the tests are taking too long and you interrupt them This is very useful if the tests are taking too long and you interrupt them
with Ctrl+C to find out where the tests are *hanging*. By default no output with Ctrl+C to find out where the tests are *hanging*. By default no output
will be shown (because KeyboardInterrupt is caught by pytest). By using this will be shown (because KeyboardInterrupt is caught by pytest). By using this
@ -226,7 +226,7 @@ to all testcases you can use ``LogXML.add_global_properties``
def start_and_prepare_env(): def start_and_prepare_env():
pass pass
class TestMe: class TestMe(object):
def test_foo(self): def test_foo(self):
assert True assert True
@ -314,7 +314,7 @@ You can specify additional plugins to ``pytest.main``::
# content of myinvoke.py # content of myinvoke.py
import pytest import pytest
class MyPlugin: class MyPlugin(object):
def pytest_sessionfinish(self): def pytest_sessionfinish(self):
print("*** test run reporting finishing") print("*** test run reporting finishing")

View File

@ -172,7 +172,7 @@ If a package is installed this way, ``pytest`` will load
.. note:: .. note::
Make sure to include ``Framework :: Pytest`` in your list of Make sure to include ``Framework :: Pytest`` in your list of
`PyPI classifiers <https://python-packaging-user-guide.readthedocs.io/en/latest/distributing/#classifiers>`_ `PyPI classifiers <https://python-packaging-user-guide.readthedocs.io/distributing/#classifiers>`_
to make it easy for users to find your plugin. to make it easy for users to find your plugin.
@ -250,8 +250,8 @@ which will import the specified module as a ``pytest`` plugin.
Plugins imported like this will automatically be marked to require Plugins imported like this will automatically be marked to require
assertion rewriting using the :func:`pytest.register_assert_rewrite` assertion rewriting using the :func:`pytest.register_assert_rewrite`
mechanism. However for this to have any effect the module must not be mechanism. However for this to have any effect the module must not be
imported already, it it was already imported at the time the imported already; if it was already imported at the time the
``pytest_plugins`` statement is processed a warning will result and ``pytest_plugins`` statement is processed, a warning will result and
assertions inside the plugin will not be re-written. To fix this you assertions inside the plugin will not be re-written. To fix this you
can either call :func:`pytest.register_assert_rewrite` yourself before can either call :func:`pytest.register_assert_rewrite` yourself before
the module is imported, or you can arrange the code to delay the the module is imported, or you can arrange the code to delay the

8
scripts/call-tox.bat Normal file
View File

@ -0,0 +1,8 @@
REM skip "coveralls" run in PRs or forks
if "%TOXENV%" == "coveralls" (
if not defined COVERALLS_REPO_TOKEN (
echo skipping coveralls run because COVERALLS_REPO_TOKEN is not defined
exit /b 0
)
)
C:\Python35\python -m tox

View File

@ -9,5 +9,8 @@ upload-dir = doc/en/build/html
[bdist_wheel] [bdist_wheel]
universal = 1 universal = 1
[metadata]
license_file = LICENSE
[devpi:upload] [devpi:upload]
formats = sdist.tgz,bdist_wheel formats = sdist.tgz,bdist_wheel

View File

@ -13,7 +13,7 @@ classifiers = ['Development Status :: 6 - Mature',
'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries',
'Topic :: Utilities'] + [ 'Topic :: Utilities'] + [
('Programming Language :: Python :: %s' % x) for x in ('Programming Language :: Python :: %s' % x) for x in
'2 2.6 2.7 3 3.3 3.4 3.5'.split()] '2 2.6 2.7 3 3.3 3.4 3.5 3.6'.split()]
with open('README.rst') as fd: with open('README.rst') as fd:
long_description = fd.read() long_description = fd.read()
@ -48,7 +48,7 @@ def has_environment_marker_support():
def main(): def main():
install_requires = ['py>=1.4.29'] # pluggy is vendored in _pytest.vendored_packages install_requires = ['py>=1.4.29', 'setuptools'] # pluggy is vendored in _pytest.vendored_packages
extras_require = {} extras_require = {}
if has_environment_marker_support(): if has_environment_marker_support():
extras_require[':python_version=="2.6"'] = ['argparse'] extras_require[':python_version=="2.6"'] = ['argparse']
@ -68,7 +68,6 @@ def main():
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
author='Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others', author='Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others',
author_email='holger at merlinux.eu',
entry_points={'console_scripts': entry_points={'console_scripts':
['pytest=pytest:main', 'py.test=pytest:main']}, ['pytest=pytest:main', 'py.test=pytest:main']},
classifiers=classifiers, classifiers=classifiers,

View File

@ -8,7 +8,7 @@ import pytest
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
class TestGeneralUsage: class TestGeneralUsage(object):
def test_config_error(self, testdir): def test_config_error(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
def pytest_configure(config): def pytest_configure(config):
@ -410,7 +410,7 @@ class TestGeneralUsage:
]) ])
class TestInvocationVariants: class TestInvocationVariants(object):
def test_earlyinit(self, testdir): def test_earlyinit(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
import pytest import pytest
@ -502,7 +502,7 @@ class TestInvocationVariants:
out, err = capsys.readouterr() out, err = capsys.readouterr()
def test_invoke_plugin_api(self, testdir, capsys): def test_invoke_plugin_api(self, testdir, capsys):
class MyPlugin: class MyPlugin(object):
def pytest_addoption(self, parser): def pytest_addoption(self, parser):
parser.addoption("--myopt") parser.addoption("--myopt")
@ -670,7 +670,7 @@ class TestInvocationVariants:
assert request.config.pluginmanager.hasplugin('python') assert request.config.pluginmanager.hasplugin('python')
class TestDurations: class TestDurations(object):
source = """ source = """
import time import time
frag = 0.002 frag = 0.002
@ -741,7 +741,7 @@ class TestDurations:
assert result.ret == 0 assert result.ret == 0
class TestDurationWithFixture: class TestDurationWithFixture(object):
source = """ source = """
import time import time
frag = 0.001 frag = 0.001

View File

@ -20,7 +20,7 @@ def test_code_gives_back_name_for_not_existing_file():
assert code.fullsource is None assert code.fullsource is None
def test_code_with_class(): def test_code_with_class():
class A: class A(object):
pass pass
pytest.raises(TypeError, "_pytest._code.Code(A)") pytest.raises(TypeError, "_pytest._code.Code(A)")
@ -136,7 +136,7 @@ def test_frame_getargs():
('z', {'c': 'd'})] ('z', {'c': 'd'})]
class TestExceptionInfo: class TestExceptionInfo(object):
def test_bad_getsource(self): def test_bad_getsource(self):
try: try:
@ -147,7 +147,7 @@ class TestExceptionInfo:
assert exci.getrepr() assert exci.getrepr()
class TestTracebackEntry: class TestTracebackEntry(object):
def test_getsource(self): def test_getsource(self):
try: try:

View File

@ -25,7 +25,7 @@ else:
import pytest import pytest
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3])) pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
class TWMock: class TWMock(object):
WRITE = object() WRITE = object()
def __init__(self): def __init__(self):
@ -89,7 +89,7 @@ def h():
g() g()
# #
class TestTraceback_f_g_h: class TestTraceback_f_g_h(object):
def setup_method(self, method): def setup_method(self, method):
try: try:
h() h()
@ -386,7 +386,7 @@ def test_match_raises_error(testdir):
"*AssertionError*Pattern*[123]*not found*", "*AssertionError*Pattern*[123]*not found*",
]) ])
class TestFormattedExcinfo: class TestFormattedExcinfo(object):
@pytest.fixture @pytest.fixture
def importasmod(self, request): def importasmod(self, request):
@ -472,7 +472,7 @@ raise ValueError()
pr = FormattedExcinfo() pr = FormattedExcinfo()
class FakeCode(object): class FakeCode(object):
class raw: class raw(object):
co_filename = '?' co_filename = '?'
path = '?' path = '?'

View File

@ -49,7 +49,7 @@ def test_source_from_function():
assert str(source).startswith('def test_source_str_function():') assert str(source).startswith('def test_source_str_function():')
def test_source_from_method(): def test_source_from_method():
class TestClass: class TestClass(object):
def test_method(self): def test_method(self):
pass pass
source = _pytest._code.Source(TestClass().test_method) source = _pytest._code.Source(TestClass().test_method)
@ -119,7 +119,7 @@ def test_isparseable():
assert not Source(" \nif 1:\npass").isparseable() assert not Source(" \nif 1:\npass").isparseable()
assert not Source(chr(0)).isparseable() assert not Source(chr(0)).isparseable()
class TestAccesses: class TestAccesses(object):
source = Source("""\ source = Source("""\
def f(x): def f(x):
pass pass
@ -143,7 +143,7 @@ class TestAccesses:
l = [x for x in self.source] l = [x for x in self.source]
assert len(l) == 4 assert len(l) == 4
class TestSourceParsingAndCompiling: class TestSourceParsingAndCompiling(object):
source = Source("""\ source = Source("""\
def f(x): def f(x):
assert (x == assert (x ==
@ -307,7 +307,7 @@ class TestSourceParsingAndCompiling:
pytest.raises(SyntaxError, _pytest._code.compile, "lambda a,a: 0", mode='eval') pytest.raises(SyntaxError, _pytest._code.compile, "lambda a,a: 0", mode='eval')
def test_getstartingblock_singleline(): def test_getstartingblock_singleline():
class A: class A(object):
def __init__(self, *args): def __init__(self, *args):
frame = sys._getframe(1) frame = sys._getframe(1)
self.source = _pytest._code.Frame(frame).statement self.source = _pytest._code.Frame(frame).statement
@ -318,7 +318,7 @@ def test_getstartingblock_singleline():
assert len(l) == 1 assert len(l) == 1
def test_getstartingblock_multiline(): def test_getstartingblock_multiline():
class A: class A(object):
def __init__(self, *args): def __init__(self, *args):
frame = sys._getframe(1) frame = sys._getframe(1)
self.source = _pytest._code.Frame(frame).statement self.source = _pytest._code.Frame(frame).statement
@ -461,16 +461,16 @@ def test_getfslineno():
assert lineno == A_lineno assert lineno == A_lineno
assert getfslineno(3) == ("", -1) assert getfslineno(3) == ("", -1)
class B: class B(object):
pass pass
B.__name__ = "B2" B.__name__ = "B2"
assert getfslineno(B)[1] == -1 assert getfslineno(B)[1] == -1
def test_code_of_object_instance_with_call(): def test_code_of_object_instance_with_call():
class A: class A(object):
pass pass
pytest.raises(TypeError, lambda: _pytest._code.Source(A())) pytest.raises(TypeError, lambda: _pytest._code.Source(A()))
class WithCall: class WithCall(object):
def __call__(self): def __call__(self):
pass pass
@ -559,7 +559,7 @@ x = 3
""") """)
assert str(source) == "raise ValueError(\n 23\n)" assert str(source) == "raise ValueError(\n 23\n)"
class TestTry: class TestTry(object):
pytestmark = astonly pytestmark = astonly
source = """\ source = """\
try: try:
@ -586,7 +586,7 @@ else:
source = getstatement(5, self.source) source = getstatement(5, self.source)
assert str(source) == " raise KeyError()" assert str(source) == " raise KeyError()"
class TestTryFinally: class TestTryFinally(object):
source = """\ source = """\
try: try:
raise ValueError raise ValueError
@ -604,7 +604,7 @@ finally:
class TestIf: class TestIf(object):
pytestmark = astonly pytestmark = astonly
source = """\ source = """\
if 1: if 1:

View File

@ -48,7 +48,7 @@ def test_str_args_deprecated(tmpdir, testdir):
from _pytest.main import EXIT_NOTESTSCOLLECTED from _pytest.main import EXIT_NOTESTSCOLLECTED
warnings = [] warnings = []
class Collect: class Collect(object):
def pytest_logwarning(self, message): def pytest_logwarning(self, message):
warnings.append(message) warnings.append(message)

View File

@ -1,5 +1,5 @@
# encoding: utf-8 # encoding: utf-8
import sys
import pytest import pytest
import doctest import doctest
@ -9,6 +9,7 @@ from decimal import Decimal
from fractions import Fraction from fractions import Fraction
inf, nan = float('inf'), float('nan') inf, nan = float('inf'), float('nan')
class MyDocTestRunner(doctest.DocTestRunner): class MyDocTestRunner(doctest.DocTestRunner):
def __init__(self): def __init__(self):
@ -19,15 +20,20 @@ class MyDocTestRunner(doctest.DocTestRunner):
example.source.strip(), got.strip(), example.want.strip())) example.source.strip(), got.strip(), example.want.strip()))
class TestApprox: class TestApprox(object):
def test_repr_string(self): def test_repr_string(self):
# Just make sure the Unicode handling doesn't raise any exceptions. # for some reason in Python 2.6 it is not displaying the tolerance representation correctly
print(approx(1.0)) plus_minus = u'\u00b1' if sys.version_info[0] > 2 else u'+-'
print(approx([1.0, 2.0, 3.0])) tol1, tol2, infr = '1.0e-06', '2.0e-06', 'inf'
print(approx(inf)) if sys.version_info[:2] == (2, 6):
print(approx(1.0, rel=nan)) tol1, tol2, infr = '???', '???', '???'
print(approx(1.0, rel=inf)) assert repr(approx(1.0)) == '1.0 {pm} {tol1}'.format(pm=plus_minus, tol1=tol1)
assert repr(approx([1.0, 2.0])) == '1.0 {pm} {tol1}, 2.0 {pm} {tol2}'.format(pm=plus_minus, tol1=tol1, tol2=tol2)
assert repr(approx(inf)) == 'inf'
assert repr(approx(1.0, rel=nan)) == '1.0 {pm} ???'.format(pm=plus_minus)
assert repr(approx(1.0, rel=inf)) == '1.0 {pm} {infr}'.format(pm=plus_minus, infr=infr)
assert repr(approx(1.0j, rel=inf)) == '1j'
def test_operator_overloading(self): def test_operator_overloading(self):
assert 1 == approx(1, rel=1e-6, abs=1e-12) assert 1 == approx(1, rel=1e-6, abs=1e-12)
@ -284,3 +290,23 @@ class TestApprox:
runner = MyDocTestRunner() runner = MyDocTestRunner()
runner.run(test) runner.run(test)
def test_unicode_plus_minus(self, testdir):
"""
Comparing approx instances inside lists should not produce an error in the detailed diff.
Integration test for issue #2111.
"""
testdir.makepyfile("""
import pytest
def test_foo():
assert [3] == [pytest.approx(4)]
""")
expected = '4.0e-06'
# for some reason in Python 2.6 it is not displaying the tolerance representation correctly
if sys.version_info[:2] == (2, 6):
expected = '???'
result = testdir.runpytest()
result.stdout.fnmatch_lines([
'*At index 0 diff: 3 != 4 * {0}'.format(expected),
'=* 1 failed in *=',
])

View File

@ -12,7 +12,7 @@ from _pytest.main import (
) )
class TestModule: class TestModule(object):
def test_failing_import(self, testdir): def test_failing_import(self, testdir):
modcol = testdir.getmodulecol("import alksdjalskdjalkjals") modcol = testdir.getmodulecol("import alksdjalskdjalkjals")
pytest.raises(Collector.CollectError, modcol.collect) pytest.raises(Collector.CollectError, modcol.collect)
@ -105,10 +105,10 @@ class TestModule:
assert name not in stdout assert name not in stdout
class TestClass: class TestClass(object):
def test_class_with_init_warning(self, testdir): def test_class_with_init_warning(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
class TestClass1: class TestClass1(object):
def __init__(self): def __init__(self):
pass pass
""") """)
@ -129,7 +129,7 @@ class TestClass:
def test_setup_teardown_class_as_classmethod(self, testdir): def test_setup_teardown_class_as_classmethod(self, testdir):
testdir.makepyfile(test_mod1=""" testdir.makepyfile(test_mod1="""
class TestClassMethod: class TestClassMethod(object):
@classmethod @classmethod
def setup_class(cls): def setup_class(cls):
pass pass
@ -167,7 +167,7 @@ class TestClass:
) )
class TestGenerator: class TestGenerator(object):
def test_generative_functions(self, testdir): def test_generative_functions(self, testdir):
modcol = testdir.getmodulecol(""" modcol = testdir.getmodulecol("""
def func1(arg, arg2): def func1(arg, arg2):
@ -192,7 +192,7 @@ class TestGenerator:
modcol = testdir.getmodulecol(""" modcol = testdir.getmodulecol("""
def func1(arg, arg2): def func1(arg, arg2):
assert arg == arg2 assert arg == arg2
class TestGenMethods: class TestGenMethods(object):
def test_gen(self): def test_gen(self):
yield func1, 17, 3*5 yield func1, 17, 3*5
yield func1, 42, 6*7 yield func1, 42, 6*7
@ -246,7 +246,7 @@ class TestGenerator:
modcol = testdir.getmodulecol(""" modcol = testdir.getmodulecol("""
def func1(arg, arg2): def func1(arg, arg2):
assert arg == arg2 assert arg == arg2
class TestGenMethods: class TestGenMethods(object):
def test_gen(self): def test_gen(self):
yield "m1", func1, 17, 3*5 yield "m1", func1, 17, 3*5
yield "m2", func1, 42, 6*7 yield "m2", func1, 42, 6*7
@ -326,7 +326,7 @@ class TestGenerator:
# has been used during collection. # has been used during collection.
o = testdir.makepyfile(""" o = testdir.makepyfile("""
setuplist = [] setuplist = []
class TestClass: class TestClass(object):
def setup_method(self, func): def setup_method(self, func):
#print "setup_method", self, func #print "setup_method", self, func
setuplist.append(self) setuplist.append(self)
@ -360,7 +360,7 @@ class TestGenerator:
assert not skipped and not failed assert not skipped and not failed
class TestFunction: class TestFunction(object):
def test_getmodulecollector(self, testdir): def test_getmodulecollector(self, testdir):
item = testdir.getitem("def test_func(): pass") item = testdir.getitem("def test_func(): pass")
modcol = item.getparent(pytest.Module) modcol = item.getparent(pytest.Module)
@ -369,7 +369,7 @@ class TestFunction:
def test_function_as_object_instance_ignored(self, testdir): def test_function_as_object_instance_ignored(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
class A: class A(object):
def __call__(self, tmpdir): def __call__(self, tmpdir):
0/0 0/0
@ -420,7 +420,7 @@ class TestFunction:
def test_issue213_parametrize_value_no_equal(self, testdir): def test_issue213_parametrize_value_no_equal(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
class A: class A(object):
def __eq__(self, other): def __eq__(self, other):
raise ValueError("not possible") raise ValueError("not possible")
@pytest.mark.parametrize('arg', [A()]) @pytest.mark.parametrize('arg', [A()])
@ -551,11 +551,11 @@ class TestFunction:
item = testdir.getitem("def test_func(): raise ValueError") item = testdir.getitem("def test_func(): raise ValueError")
config = item.config config = item.config
class MyPlugin1: class MyPlugin1(object):
def pytest_pyfunc_call(self, pyfuncitem): def pytest_pyfunc_call(self, pyfuncitem):
raise ValueError raise ValueError
class MyPlugin2: class MyPlugin2(object):
def pytest_pyfunc_call(self, pyfuncitem): def pytest_pyfunc_call(self, pyfuncitem):
return True return True
@ -683,7 +683,7 @@ class TestFunction:
assert [x.originalname for x in items] == ['test_func', 'test_func'] assert [x.originalname for x in items] == ['test_func', 'test_func']
class TestSorting: class TestSorting(object):
def test_check_equality(self, testdir): def test_check_equality(self, testdir):
modcol = testdir.getmodulecol(""" modcol = testdir.getmodulecol("""
def test_pass(): pass def test_pass(): pass
@ -733,7 +733,7 @@ class TestSorting:
assert [item.name for item in colitems] == ['test_b', 'test_a'] assert [item.name for item in colitems] == ['test_b', 'test_a']
class TestConftestCustomization: class TestConftestCustomization(object):
def test_pytest_pycollect_module(self, testdir): def test_pytest_pycollect_module(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
import pytest import pytest
@ -847,7 +847,7 @@ def test_modulecol_roundtrip(testdir):
assert modcol.name == newcol.name assert modcol.name == newcol.name
class TestTracebackCutting: class TestTracebackCutting(object):
def test_skip_simple(self): def test_skip_simple(self):
excinfo = pytest.raises(pytest.skip.Exception, 'pytest.skip("xxx")') excinfo = pytest.raises(pytest.skip.Exception, 'pytest.skip("xxx")')
assert excinfo.traceback[-1].frame.code.name == "skip" assert excinfo.traceback[-1].frame.code.name == "skip"
@ -973,7 +973,7 @@ class TestTracebackCutting:
assert filter_traceback(tb[-1]) assert filter_traceback(tb[-1])
class TestReportInfo: class TestReportInfo(object):
def test_itemreport_reportinfo(self, testdir, linecomp): def test_itemreport_reportinfo(self, testdir, linecomp):
testdir.makeconftest(""" testdir.makeconftest("""
import pytest import pytest
@ -998,7 +998,7 @@ class TestReportInfo:
def test_class_reportinfo(self, testdir): def test_class_reportinfo(self, testdir):
modcol = testdir.getmodulecol(""" modcol = testdir.getmodulecol("""
# lineno 0 # lineno 0
class TestClass: class TestClass(object):
def test_hello(self): pass def test_hello(self): pass
""") """)
classcol = testdir.collect_by_name(modcol, "TestClass") classcol = testdir.collect_by_name(modcol, "TestClass")
@ -1033,7 +1033,7 @@ class TestReportInfo:
def check(x): def check(x):
pass pass
yield check, 3 yield check, 3
class TestClass: class TestClass(object):
def test_method(self): def test_method(self):
pass pass
""" """
@ -1042,7 +1042,7 @@ class TestReportInfo:
# https://github.com/pytest-dev/pytest/issues/1204 # https://github.com/pytest-dev/pytest/issues/1204
modcol = testdir.getmodulecol(""" modcol = testdir.getmodulecol("""
# lineno 0 # lineno 0
class TestClass: class TestClass(object):
def __getattr__(self, name): def __getattr__(self, name):
return "this is not an int" return "this is not an int"
@ -1064,7 +1064,7 @@ def test_customized_python_discovery(testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
def check_simple(): def check_simple():
pass pass
class CheckMyApp: class CheckMyApp(object):
def check_meth(self): def check_meth(self):
pass pass
""") """)
@ -1139,7 +1139,7 @@ def test_customize_through_attributes(testdir):
return MyClass(name, parent=collector) return MyClass(name, parent=collector)
""") """)
testdir.makepyfile(""" testdir.makepyfile("""
class MyTestClass: class MyTestClass(object):
def test_hello(self): def test_hello(self):
pass pass
""") """)
@ -1153,11 +1153,11 @@ def test_customize_through_attributes(testdir):
def test_unorderable_types(testdir): def test_unorderable_types(testdir):
testdir.makepyfile(""" testdir.makepyfile("""
class TestJoinEmpty: class TestJoinEmpty(object):
pass pass
def make_test(): def make_test():
class Test: class Test(object):
pass pass
Test.__name__ = "TestFoo" Test.__name__ = "TestFoo"
return Test return Test

View File

@ -20,7 +20,7 @@ def test_getfuncargnames():
def h(arg1, arg2, arg3="hello"): pass def h(arg1, arg2, arg3="hello"): pass
assert fixtures.getfuncargnames(h) == ('arg1', 'arg2') assert fixtures.getfuncargnames(h) == ('arg1', 'arg2')
class A: class A(object):
def f(self, arg1, arg2="hello"): def f(self, arg1, arg2="hello"):
pass pass
@ -28,7 +28,7 @@ def test_getfuncargnames():
if sys.version_info < (3,0): if sys.version_info < (3,0):
assert fixtures.getfuncargnames(A.f) == ('arg1',) assert fixtures.getfuncargnames(A.f) == ('arg1',)
class TestFillFixtures: class TestFillFixtures(object):
def test_fillfuncargs_exposed(self): def test_fillfuncargs_exposed(self):
# used by oejskit, kept for compatibility # used by oejskit, kept for compatibility
assert pytest._fillfuncargs == fixtures.fillfixtures assert pytest._fillfuncargs == fixtures.fillfixtures
@ -79,7 +79,7 @@ class TestFillFixtures:
def something(request): def something(request):
return request.function.__name__ return request.function.__name__
class TestClass: class TestClass(object):
def test_method(self, something): def test_method(self, something):
assert something == "test_method" assert something == "test_method"
def test_func(something): def test_func(something):
@ -91,7 +91,7 @@ class TestFillFixtures:
def test_funcarg_lookup_classlevel(self, testdir): def test_funcarg_lookup_classlevel(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
import pytest import pytest
class TestClass: class TestClass(object):
@pytest.fixture @pytest.fixture
def something(self, request): def something(self, request):
@ -134,7 +134,7 @@ class TestFillFixtures:
def spam(): def spam():
return 'spam' return 'spam'
class TestSpam: class TestSpam(object):
@pytest.fixture @pytest.fixture
def spam(self, spam): def spam(self, spam):
@ -463,7 +463,7 @@ class TestFillFixtures:
assert result.ret == 0 assert result.ret == 0
class TestRequestBasic: class TestRequestBasic(object):
def test_request_attributes(self, testdir): def test_request_attributes(self, testdir):
item = testdir.getitem(""" item = testdir.getitem("""
import pytest import pytest
@ -484,7 +484,7 @@ class TestRequestBasic:
def test_request_attributes_method(self, testdir): def test_request_attributes_method(self, testdir):
item, = testdir.getitems(""" item, = testdir.getitems("""
import pytest import pytest
class TestB: class TestB(object):
@pytest.fixture @pytest.fixture
def something(self, request): def something(self, request):
@ -502,7 +502,7 @@ class TestRequestBasic:
@pytest.fixture @pytest.fixture
def something(request): def something(request):
pass pass
class TestClass: class TestClass(object):
def test_method(self, something): def test_method(self, something):
pass pass
""") """)
@ -704,7 +704,7 @@ class TestRequestBasic:
def test_func(): def test_func():
pass pass
class TestClass: class TestClass(object):
@pytest.fixture(scope="class", autouse=True) @pytest.fixture(scope="class", autouse=True)
def setup_class(self): def setup_class(self):
l.append("class") l.append("class")
@ -771,7 +771,7 @@ class TestRequestBasic:
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=2) reprec.assertoutcome(passed=2)
class TestRequestMarking: class TestRequestMarking(object):
def test_applymarker(self, testdir): def test_applymarker(self, testdir):
item1,item2 = testdir.getitems(""" item1,item2 = testdir.getitems("""
import pytest import pytest
@ -779,7 +779,7 @@ class TestRequestMarking:
@pytest.fixture @pytest.fixture
def something(request): def something(request):
pass pass
class TestClass: class TestClass(object):
def test_func1(self, something): def test_func1(self, something):
pass pass
def test_func2(self, something): def test_func2(self, something):
@ -831,7 +831,7 @@ class TestRequestMarking:
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=2) reprec.assertoutcome(passed=2)
class TestRequestCachedSetup: class TestRequestCachedSetup(object):
def test_request_cachedsetup_defaultmodule(self, testdir): def test_request_cachedsetup_defaultmodule(self, testdir):
reprec = testdir.inline_runsource(""" reprec = testdir.inline_runsource("""
mysetup = ["hello",].pop mysetup = ["hello",].pop
@ -844,7 +844,7 @@ class TestRequestCachedSetup:
def test_func1(something): def test_func1(something):
assert something == "hello" assert something == "hello"
class TestClass: class TestClass(object):
def test_func1a(self, something): def test_func1a(self, something):
assert something == "hello" assert something == "hello"
""") """)
@ -862,7 +862,7 @@ class TestRequestCachedSetup:
assert something == "hello3" assert something == "hello3"
def test_func2(something): def test_func2(something):
assert something == "hello2" assert something == "hello2"
class TestClass: class TestClass(object):
def test_func1a(self, something): def test_func1a(self, something):
assert something == "hello" assert something == "hello"
def test_func2b(self, something): def test_func2b(self, something):
@ -996,7 +996,7 @@ class TestRequestCachedSetup:
"*ZeroDivisionError*", "*ZeroDivisionError*",
]) ])
class TestFixtureUsages: class TestFixtureUsages(object):
def test_noargfixturedec(self, testdir): def test_noargfixturedec(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@ -1138,7 +1138,7 @@ class TestFixtureUsages:
def test_factory_setup_as_classes_fails(self, testdir): def test_factory_setup_as_classes_fails(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
class arg1: class arg1(object):
def __init__(self, request): def __init__(self, request):
self.x = 1 self.x = 1
arg1 = pytest.fixture()(arg1) arg1 = pytest.fixture()(arg1)
@ -1172,7 +1172,7 @@ class TestFixtureUsages:
request.cls.hello = "world" request.cls.hello = "world"
l.append(1) l.append(1)
class TestClass: class TestClass(object):
def test_one(self): def test_one(self):
assert self.hello == "world" assert self.hello == "world"
assert len(l) == 1 assert len(l) == 1
@ -1198,7 +1198,7 @@ class TestFixtureUsages:
""") """)
testdir.makepyfile(""" testdir.makepyfile("""
class TestClass: class TestClass(object):
def test_one(self): def test_one(self):
assert self.hello == "world" assert self.hello == "world"
def test_two(self): def test_two(self):
@ -1217,7 +1217,7 @@ class TestFixtureUsages:
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
class TestClass: class TestClass(object):
@pytest.fixture @pytest.fixture
def setup1(self, request): def setup1(self, request):
assert self == request.instance assert self == request.instance
@ -1256,7 +1256,7 @@ class TestFixtureUsages:
assert l == [1,2, 10,20] assert l == [1,2, 10,20]
class TestFixtureManagerParseFactories: class TestFixtureManagerParseFactories(object):
@pytest.fixture @pytest.fixture
def testdir(self, request): def testdir(self, request):
@ -1280,7 +1280,7 @@ class TestFixtureManagerParseFactories:
def test_parsefactories_evil_objects_issue214(self, testdir): def test_parsefactories_evil_objects_issue214(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
class A: class A(object):
def __call__(self): def __call__(self):
pass pass
def __getattr__(self, name): def __getattr__(self, name):
@ -1311,7 +1311,7 @@ class TestFixtureManagerParseFactories:
@pytest.fixture @pytest.fixture
def hello(request): def hello(request):
return "module" return "module"
class TestClass: class TestClass(object):
@pytest.fixture @pytest.fixture
def hello(self, request): def hello(self, request):
return "class" return "class"
@ -1360,7 +1360,7 @@ class TestFixtureManagerParseFactories:
reprec.assertoutcome(passed=2) reprec.assertoutcome(passed=2)
class TestAutouseDiscovery: class TestAutouseDiscovery(object):
@pytest.fixture @pytest.fixture
def testdir(self, testdir): def testdir(self, testdir):
@ -1402,14 +1402,14 @@ class TestAutouseDiscovery:
def test_two_classes_separated_autouse(self, testdir): def test_two_classes_separated_autouse(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
class TestA: class TestA(object):
l = [] l = []
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def setup1(self): def setup1(self):
self.l.append(1) self.l.append(1)
def test_setup1(self): def test_setup1(self):
assert self.l == [1] assert self.l == [1]
class TestB: class TestB(object):
l = [] l = []
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def setup2(self): def setup2(self):
@ -1423,7 +1423,7 @@ class TestAutouseDiscovery:
def test_setup_at_classlevel(self, testdir): def test_setup_at_classlevel(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
class TestClass: class TestClass(object):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def permethod(self, request): def permethod(self, request):
request.instance.funcname = request.function.__name__ request.instance.funcname = request.function.__name__
@ -1505,13 +1505,13 @@ class TestAutouseDiscovery:
def test_x(): def test_x():
assert l == ["module"] assert l == ["module"]
class TestA: class TestA(object):
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def append2(self): def append2(self):
l.append("A") l.append("A")
def test_hello(self): def test_hello(self):
assert l == ["module", "module", "A"], l assert l == ["module", "module", "A"], l
class TestA2: class TestA2(object):
def test_world(self): def test_world(self):
assert l == ["module", "module", "A", "module"], l assert l == ["module", "module", "A", "module"], l
""") """)
@ -1519,7 +1519,7 @@ class TestAutouseDiscovery:
reprec.assertoutcome(passed=3) reprec.assertoutcome(passed=3)
class TestAutouseManagement: class TestAutouseManagement(object):
def test_autouse_conftest_mid_directory(self, testdir): def test_autouse_conftest_mid_directory(self, testdir):
pkgdir = testdir.mkpydir("xyz123") pkgdir = testdir.mkpydir("xyz123")
pkgdir.join("conftest.py").write(_pytest._code.Source(""" pkgdir.join("conftest.py").write(_pytest._code.Source("""
@ -1654,10 +1654,10 @@ class TestAutouseManagement:
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
class TestClass: class TestClass(object):
def test_1(self): def test_1(self):
pass pass
class TestClass2: class TestClass2(object):
def test_2(self): def test_2(self):
pass pass
""") """)
@ -1682,7 +1682,7 @@ class TestAutouseManagement:
def mappend(): def mappend():
l.append(1) l.append(1)
class TestHallo: class TestHallo(object):
def test_method(self): def test_method(self):
assert l == [1,3,2] assert l == [1,3,2]
""") """)
@ -1696,7 +1696,7 @@ class TestAutouseManagement:
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
if metafunc.cls is not None: if metafunc.cls is not None:
metafunc.parametrize("item", [1,2], scope="class") metafunc.parametrize("item", [1,2], scope="class")
class TestClass: class TestClass(object):
@pytest.fixture(scope="class", autouse=True) @pytest.fixture(scope="class", autouse=True)
def addteardown(self, item, request): def addteardown(self, item, request):
l.append("setup-%d" % item) l.append("setup-%d" % item)
@ -1756,7 +1756,7 @@ class TestAutouseManagement:
reprec.assertoutcome(passed=2) reprec.assertoutcome(passed=2)
class TestFixtureMarker: class TestFixtureMarker(object):
def test_parametrize(self, testdir): def test_parametrize(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@ -1822,7 +1822,7 @@ class TestFixtureMarker:
def test_2(arg): def test_2(arg):
assert arg == 1 assert arg == 1
assert len(l) == 1 assert len(l) == 1
class TestClass: class TestClass(object):
def test3(self, arg): def test3(self, arg):
assert arg == 1 assert arg == 1
assert len(l) == 1 assert len(l) == 1
@ -1916,7 +1916,7 @@ class TestFixtureMarker:
def test_2(arg): def test_2(arg):
assert arg == 1 assert arg == 1
assert len(l) == 1 assert len(l) == 1
class TestClass: class TestClass(object):
def test3(self, arg): def test3(self, arg):
assert arg == 1 assert arg == 1
assert len(l) == 1 assert len(l) == 1
@ -2135,12 +2135,12 @@ class TestFixtureMarker:
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
class TestClass2: class TestClass2(object):
def test_1(self): def test_1(self):
pass pass
def test_2(self): def test_2(self):
pass pass
class TestClass: class TestClass(object):
def test_3(self): def test_3(self):
pass pass
""") """)
@ -2213,7 +2213,7 @@ class TestFixtureMarker:
l = [] l = []
class TestClass: class TestClass(object):
@classmethod @classmethod
@pytest.fixture(scope="class", autouse=True) @pytest.fixture(scope="class", autouse=True)
def setup1(self, request, param1): def setup1(self, request, param1):
@ -2273,7 +2273,7 @@ class TestFixtureMarker:
testpath = testdir.makepyfile(""" testpath = testdir.makepyfile("""
import pytest import pytest
class Box: class Box(object):
value = 0 value = 0
@pytest.fixture(scope='class') @pytest.fixture(scope='class')
@ -2284,11 +2284,11 @@ class TestFixtureMarker:
def test_a(a): def test_a(a):
assert a == 1 assert a == 1
class Test1: class Test1(object):
def test_b(self, a): def test_b(self, a):
assert a == 2 assert a == 2
class Test2: class Test2(object):
def test_c(self, a): def test_c(self, a):
assert a == 3""") assert a == 3""")
reprec = testdir.inline_run(testpath) reprec = testdir.inline_run(testpath)
@ -2402,11 +2402,11 @@ class TestFixtureMarker:
request.addfinalizer(lambda: l.append("fin %s" % request.param)) request.addfinalizer(lambda: l.append("fin %s" % request.param))
return request.param return request.param
class TestGreetings: class TestGreetings(object):
def test_hello(self, human): def test_hello(self, human):
l.append("test_hello") l.append("test_hello")
class TestMetrics: class TestMetrics(object):
def test_name(self, human): def test_name(self, human):
l.append("test_name") l.append("test_name")
@ -2499,7 +2499,7 @@ class TestFixtureMarker:
'*test_foo*beta*']) '*test_foo*beta*'])
class TestRequestScopeAccess: class TestRequestScopeAccess(object):
pytestmark = pytest.mark.parametrize(("scope", "ok", "error"),[ pytestmark = pytest.mark.parametrize(("scope", "ok", "error"),[
["session", "", "fspath class function module"], ["session", "", "fspath class function module"],
["module", "module fspath", "cls function"], ["module", "module fspath", "cls function"],
@ -2543,7 +2543,7 @@ class TestRequestScopeAccess:
reprec = testdir.inline_run() reprec = testdir.inline_run()
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
class TestErrors: class TestErrors(object):
def test_subfactory_missing_funcarg(self, testdir): def test_subfactory_missing_funcarg(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@ -2607,7 +2607,7 @@ class TestErrors:
"*1 error*", "*1 error*",
]) ])
class TestShowFixtures: class TestShowFixtures(object):
def test_funcarg_compat(self, testdir): def test_funcarg_compat(self, testdir):
config = testdir.parseconfigure("--funcargs") config = testdir.parseconfigure("--funcargs")
assert config.option.showfixtures assert config.option.showfixtures
@ -2770,7 +2770,7 @@ class TestShowFixtures:
@pytest.mark.parametrize('flavor', ['fixture', 'yield_fixture']) @pytest.mark.parametrize('flavor', ['fixture', 'yield_fixture'])
class TestContextManagerFixtureFuncs: class TestContextManagerFixtureFuncs(object):
def test_simple(self, testdir, flavor): def test_simple(self, testdir, flavor):
testdir.makepyfile(""" testdir.makepyfile("""
@ -2877,7 +2877,7 @@ class TestContextManagerFixtureFuncs:
result = testdir.runpytest("-s") result = testdir.runpytest("-s")
result.stdout.fnmatch_lines("*mew*") result.stdout.fnmatch_lines("*mew*")
class TestParameterizedSubRequest: class TestParameterizedSubRequest(object):
def test_call_from_fixture(self, testdir): def test_call_from_fixture(self, testdir):
testfile = testdir.makepyfile(""" testfile = testdir.makepyfile("""
import pytest import pytest

View File

@ -3,7 +3,7 @@ from _pytest import python
from _pytest import runner from _pytest import runner
class TestOEJSKITSpecials: class TestOEJSKITSpecials(object):
def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage
testdir.makeconftest(""" testdir.makeconftest("""
import pytest import pytest
@ -19,7 +19,7 @@ class TestOEJSKITSpecials:
@pytest.fixture @pytest.fixture
def arg1(request): def arg1(request):
return 42 return 42
class MyClass: class MyClass(object):
pass pass
""") """)
# this hook finds funcarg factories # this hook finds funcarg factories
@ -48,7 +48,7 @@ class TestOEJSKITSpecials:
@pytest.fixture @pytest.fixture
def arg1(request): def arg1(request):
return 42 return 42
class MyClass: class MyClass(object):
pass pass
""") """)
# this hook finds funcarg factories # this hook finds funcarg factories
@ -76,7 +76,7 @@ def test_wrapped_getfslineno():
fs2, lineno2 = python.getfslineno(wrap) fs2, lineno2 = python.getfslineno(wrap)
assert lineno > lineno2, "getfslineno does not unwrap correctly" assert lineno > lineno2, "getfslineno does not unwrap correctly"
class TestMockDecoration: class TestMockDecoration(object):
def test_wrapped_getfuncargnames(self): def test_wrapped_getfuncargnames(self):
from _pytest.compat import getfuncargnames from _pytest.compat import getfuncargnames
@ -207,7 +207,7 @@ class TestMockDecoration:
@patch('os.getcwd') @patch('os.getcwd')
@patch('os.path') @patch('os.path')
@mark.slow @mark.slow
class TestSimple: class TestSimple(object):
def test_simple_thing(self, mock_path, mock_getcwd): def test_simple_thing(self, mock_path, mock_getcwd):
pass pass
""") """)
@ -215,7 +215,7 @@ class TestMockDecoration:
reprec.assertoutcome(passed=1) reprec.assertoutcome(passed=1)
class TestReRunTests: class TestReRunTests(object):
def test_rerun(self, testdir): def test_rerun(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
from _pytest.runner import runtestprotocol from _pytest.runner import runtestprotocol
@ -251,7 +251,7 @@ def test_pytestconfig_is_session_scoped():
assert pytestconfig._pytestfixturefunction.scope == "session" assert pytestconfig._pytestfixturefunction.scope == "session"
class TestNoselikeTestAttribute: class TestNoselikeTestAttribute(object):
def test_module_with_global_test(self, testdir): def test_module_with_global_test(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
__test__ = False __test__ = False
@ -270,7 +270,7 @@ class TestNoselikeTestAttribute:
pass pass
test_func.__test__ = False test_func.__test__ = False
class TestSome: class TestSome(object):
__test__ = False __test__ = False
def test_method(self): def test_method(self):
pass pass
@ -328,7 +328,7 @@ class TestNoselikeTestAttribute:
@pytest.mark.issue351 @pytest.mark.issue351
class TestParameterize: class TestParameterize(object):
def test_idfn_marker(self, testdir): def test_idfn_marker(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""

View File

@ -13,12 +13,12 @@ from hypothesis import strategies
PY3 = sys.version_info >= (3, 0) PY3 = sys.version_info >= (3, 0)
class TestMetafunc: class TestMetafunc(object):
def Metafunc(self, func): def Metafunc(self, func):
# the unit tests of this class check if things work correctly # the unit tests of this class check if things work correctly
# on the funcarg level, so we don't need a full blown # on the funcarg level, so we don't need a full blown
# initiliazation # initiliazation
class FixtureInfo: class FixtureInfo(object):
name2fixturedefs = None name2fixturedefs = None
def __init__(self, names): def __init__(self, names):
@ -68,7 +68,7 @@ class TestMetafunc:
def func(arg1): pass def func(arg1): pass
metafunc = self.Metafunc(func) metafunc = self.Metafunc(func)
class obj: pass class obj(object): pass
metafunc.addcall(param=obj) metafunc.addcall(param=obj)
metafunc.addcall(param=obj) metafunc.addcall(param=obj)
@ -83,7 +83,7 @@ class TestMetafunc:
metafunc = self.Metafunc(func) metafunc = self.Metafunc(func)
class obj: pass class obj(object): pass
metafunc.addcall(funcargs={"x": 2}) metafunc.addcall(funcargs={"x": 2})
metafunc.addcall(funcargs={"x": 3}) metafunc.addcall(funcargs={"x": 3})
@ -150,7 +150,7 @@ class TestMetafunc:
def func(x, y): pass def func(x, y): pass
metafunc = self.Metafunc(func) metafunc = self.Metafunc(func)
class A: class A(object):
pass pass
metafunc.parametrize("x", [A(), A()]) metafunc.parametrize("x", [A(), A()])
@ -296,18 +296,58 @@ class TestMetafunc:
@pytest.mark.issue351 @pytest.mark.issue351
def test_idmaker_idfn_exception(self): def test_idmaker_idfn_exception(self):
from _pytest.python import idmaker from _pytest.python import idmaker
from _pytest.recwarn import WarningsRecorder
class BadIdsException(Exception):
pass
def ids(val): def ids(val):
raise Exception("bad code") raise BadIdsException("ids raised")
result = idmaker(("a", "b"), [(10.0, IndexError()), rec = WarningsRecorder()
(20, KeyError()), with rec:
("three", [1, 2, 3]), idmaker(("a", "b"), [(10.0, IndexError()),
], idfn=ids) (20, KeyError()),
assert result == ["10.0-b0", ("three", [1, 2, 3]),
"20-b1", ], idfn=ids)
"three-b2",
] assert [str(i.message) for i in rec.list] == [
"Raised while trying to determine id of parameter a at position 0."
"\nUpdate your code as this will raise an error in pytest-4.0.",
"Raised while trying to determine id of parameter b at position 0."
"\nUpdate your code as this will raise an error in pytest-4.0.",
"Raised while trying to determine id of parameter a at position 1."
"\nUpdate your code as this will raise an error in pytest-4.0.",
"Raised while trying to determine id of parameter b at position 1."
"\nUpdate your code as this will raise an error in pytest-4.0.",
"Raised while trying to determine id of parameter a at position 2."
"\nUpdate your code as this will raise an error in pytest-4.0.",
"Raised while trying to determine id of parameter b at position 2."
"\nUpdate your code as this will raise an error in pytest-4.0.",
]
def test_parametrize_ids_exception(self, testdir):
"""
:param testdir: the instance of Testdir class, a temporary
test directory.
"""
testdir.makepyfile("""
import pytest
def ids(arg):
raise Exception("bad ids")
@pytest.mark.parametrize("arg", ["a", "b"], ids=ids)
def test_foo(arg):
pass
""")
result = testdir.runpytest("--collect-only")
result.stdout.fnmatch_lines([
"<Module 'test_parametrize_ids_exception.py'>",
" <Function 'test_foo[a]'>",
" <Function 'test_foo[b]'>",
])
def test_idmaker_with_ids(self): def test_idmaker_with_ids(self):
from _pytest.python import idmaker from _pytest.python import idmaker
@ -561,7 +601,7 @@ class TestMetafunc:
pytestmark = pytest.mark.parametrize("x", [1,2]) pytestmark = pytest.mark.parametrize("x", [1,2])
def test_func(x): def test_func(x):
assert 0, x assert 0, x
class TestClass: class TestClass(object):
pytestmark = pytest.mark.parametrize("y", [3,4]) pytestmark = pytest.mark.parametrize("y", [3,4])
def test_meth(self, x, y): def test_meth(self, x, y):
assert 0, x assert 0, x
@ -632,7 +672,7 @@ class TestMetafunc:
assert fixtures._format_args(function4) == "(arg1, *args, **kwargs)" assert fixtures._format_args(function4) == "(arg1, *args, **kwargs)"
class TestMetafuncFunctional: class TestMetafuncFunctional(object):
def test_attributes(self, testdir): def test_attributes(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
# assumes that generate/provide runs in the same process # assumes that generate/provide runs in the same process
@ -651,7 +691,7 @@ class TestMetafuncFunctional:
assert metafunc.function == test_function assert metafunc.function == test_function
assert metafunc.cls is None assert metafunc.cls is None
class TestClass: class TestClass(object):
def test_method(self, metafunc, pytestconfig): def test_method(self, metafunc, pytestconfig):
assert metafunc.config == pytestconfig assert metafunc.config == pytestconfig
assert metafunc.module.__name__ == __name__ assert metafunc.module.__name__ == __name__
@ -676,7 +716,7 @@ class TestMetafuncFunctional:
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
metafunc.addcall(funcargs=dict(arg1=1, arg2=1)) metafunc.addcall(funcargs=dict(arg1=1, arg2=1))
class TestClass: class TestClass(object):
def test_myfunc(self, arg1, arg2): def test_myfunc(self, arg1, arg2):
assert arg1 == arg2 assert arg1 == arg2
""") """)
@ -716,7 +756,7 @@ class TestMetafuncFunctional:
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
assert 'xyz' not in metafunc.fixturenames assert 'xyz' not in metafunc.fixturenames
class TestHello: class TestHello(object):
def test_hello(xyz): def test_hello(xyz):
pass pass
""") """)
@ -742,7 +782,7 @@ class TestMetafuncFunctional:
def arg2(request): def arg2(request):
return request.param[1] return request.param[1]
class TestClass: class TestClass(object):
def test_myfunc(self, arg1, arg2): def test_myfunc(self, arg1, arg2):
assert arg1 == arg2 assert arg1 == arg2
""") """)
@ -755,7 +795,7 @@ class TestMetafuncFunctional:
def test_generate_tests_in_class(self, testdir): def test_generate_tests_in_class(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
class TestClass: class TestClass(object):
def pytest_generate_tests(self, metafunc): def pytest_generate_tests(self, metafunc):
metafunc.addcall(funcargs={'hello': 'world'}, id="hello") metafunc.addcall(funcargs={'hello': 'world'}, id="hello")
@ -774,7 +814,7 @@ class TestMetafuncFunctional:
metafunc.addcall({'arg1': 10}) metafunc.addcall({'arg1': 10})
metafunc.addcall({'arg1': 20}) metafunc.addcall({'arg1': 20})
class TestClass: class TestClass(object):
def test_func(self, arg1): def test_func(self, arg1):
assert not hasattr(self, 'x') assert not hasattr(self, 'x')
self.x = 1 self.x = 1
@ -791,7 +831,7 @@ class TestMetafuncFunctional:
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
metafunc.addcall({'arg1': 1}) metafunc.addcall({'arg1': 1})
class TestClass: class TestClass(object):
def test_method(self, arg1): def test_method(self, arg1):
assert arg1 == self.val assert arg1 == self.val
def setup_method(self, func): def setup_method(self, func):
@ -1077,7 +1117,7 @@ class TestMetafuncFunctional:
assert expectederror in failures[0].longrepr.reprcrash.message assert expectederror in failures[0].longrepr.reprcrash.message
class TestMetafuncFunctionalAuto: class TestMetafuncFunctionalAuto(object):
""" """
Tests related to automatically find out the correct scope for parametrized tests (#1832). Tests related to automatically find out the correct scope for parametrized tests (#1832).
""" """
@ -1196,7 +1236,7 @@ class TestMetafuncFunctionalAuto:
assert output.count('preparing foo-3') == 1 assert output.count('preparing foo-3') == 1
class TestMarkersWithParametrization: class TestMarkersWithParametrization(object):
pytestmark = pytest.mark.issue308 pytestmark = pytest.mark.issue308
def test_simple_mark(self, testdir): def test_simple_mark(self, testdir):
s = """ s = """
@ -1415,3 +1455,26 @@ class TestMarkersWithParametrization:
"*test_func*0*PASS*", "*test_func*0*PASS*",
"*test_func*2*PASS*", "*test_func*2*PASS*",
]) ])
def test_pytest_make_parametrize_id_with_argname(self, testdir):
testdir.makeconftest("""
def pytest_make_parametrize_id(config, val, argname):
return str(val * 2 if argname == 'x' else val * 10)
""")
testdir.makepyfile("""
import pytest
@pytest.mark.parametrize("x", range(2))
def test_func_a(x):
pass
@pytest.mark.parametrize("y", [1])
def test_func_b(y):
pass
""")
result = testdir.runpytest("-v")
result.stdout.fnmatch_lines([
"*test_func_a*0*PASS*",
"*test_func_a*2*PASS*",
"*test_func_b*10*PASS*",
])

View File

@ -2,7 +2,7 @@ import pytest
import sys import sys
class TestRaises: class TestRaises(object):
def test_raises(self): def test_raises(self):
source = "int('qwe')" source = "int('qwe')"
excinfo = pytest.raises(ValueError, source) excinfo = pytest.raises(ValueError, source)
@ -20,7 +20,7 @@ class TestRaises:
pytest.raises(ValueError, int, 'hello') pytest.raises(ValueError, int, 'hello')
def test_raises_callable_no_exception(self): def test_raises_callable_no_exception(self):
class A: class A(object):
def __call__(self): def __call__(self):
pass pass
try: try:
@ -118,3 +118,18 @@ class TestRaises:
for o in gc.get_objects(): for o in gc.get_objects():
assert type(o) is not T assert type(o) is not T
def test_raises_match(self):
msg = r"with base \d+"
with pytest.raises(ValueError, match=msg):
int('asdf')
msg = "with base 10"
with pytest.raises(ValueError, match=msg):
int('asdf')
msg = "with base 16"
expr = r"Pattern '{0}' not found in 'invalid literal for int\(\) with base 10: 'asdf''".format(msg)
with pytest.raises(AssertionError, match=expr):
with pytest.raises(ValueError, match=msg):
int('asdf', base=10)

Some files were not shown because too many files have changed in this diff Show More