Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba6ecc14c8 | ||
|
|
41d3b3f4f9 | ||
|
|
dda17994ec | ||
|
|
427cee109a | ||
|
|
30d459e2e3 | ||
|
|
f31447b82b | ||
|
|
b071fdc633 | ||
|
|
19766ef0bc | ||
|
|
94155ee62a | ||
|
|
835328d862 | ||
|
|
8dc497b54b | ||
|
|
da201c7d29 | ||
|
|
01068e5571 | ||
|
|
73cab77249 | ||
|
|
09bcf7f170 | ||
|
|
f1c4cfea2c | ||
|
|
e5deb8a927 | ||
|
|
24db3c123d | ||
|
|
940ed7e943 | ||
|
|
f279bd2bd5 | ||
|
|
6db2f315fa | ||
|
|
7660a19d0a | ||
|
|
8cba033bbb | ||
|
|
6b56c36ae7 | ||
|
|
65be1231b1 | ||
|
|
8639bf7554 | ||
|
|
f484e7c9ca | ||
|
|
2bafa74765 | ||
|
|
77d842ceb2 | ||
|
|
863b7d0c50 | ||
|
|
40ec35767f | ||
|
|
04cf5e1df4 | ||
|
|
138e255631 | ||
|
|
51378aba01 | ||
|
|
8e67dd13e7 | ||
|
|
99efc281ee | ||
|
|
a2b8981b50 | ||
|
|
e21ae3991d | ||
|
|
8a41b26f56 | ||
|
|
b6f766ae87 | ||
|
|
ceeb5149f3 | ||
|
|
b38cf77562 | ||
|
|
c60854474a | ||
|
|
c69fbd79a9 | ||
|
|
183f3737d4 | ||
|
|
1e10de574d | ||
|
|
722f9eadcd | ||
|
|
7a4fe7582c | ||
|
|
96a1d2941b | ||
|
|
ee284ec587 | ||
|
|
927f411ee2 | ||
|
|
919f50a3bd | ||
|
|
4e58c9a7d0 | ||
|
|
a9f3053f72 | ||
|
|
d512e7f26b | ||
|
|
f985f47a02 | ||
|
|
4c45b93007 | ||
|
|
a094fb3aa6 | ||
|
|
e43d1174f7 | ||
|
|
696a9112be | ||
|
|
be08223d5a | ||
|
|
10b3274924 | ||
|
|
67ba8aaaa2 | ||
|
|
edf8283bd8 | ||
|
|
c8a366e551 | ||
|
|
4eeb475138 | ||
|
|
82218e4ee1 | ||
|
|
8593bb12ee | ||
|
|
e75078eae2 | ||
|
|
519f02b014 | ||
|
|
cb7c472e34 | ||
|
|
a947e83be9 | ||
|
|
e92d373460 | ||
|
|
86b8801470 | ||
|
|
856ad719d3 | ||
|
|
9c45d6cd83 | ||
|
|
a152ea2dbb | ||
|
|
8f516d27fa | ||
|
|
3345ac9216 | ||
|
|
972a5fb5d5 | ||
|
|
ea0febad28 | ||
|
|
ecb20b96fc | ||
|
|
49fc4e5e4c | ||
|
|
3c866e7080 | ||
|
|
df200297e2 | ||
|
|
7538680c98 | ||
|
|
d3f4b3d14a | ||
|
|
a1597aca89 | ||
|
|
7d498fdc82 | ||
|
|
2f11a85698 | ||
|
|
847067fb02 | ||
|
|
1673667232 | ||
|
|
53a0e2b118 | ||
|
|
d99ceb1218 | ||
|
|
b54ea74d4d | ||
|
|
6a8160b318 | ||
|
|
c3148d1d6b | ||
|
|
b0ede044ac | ||
|
|
3fbf2e7a80 | ||
|
|
af0ec120fe | ||
|
|
678750c8f8 | ||
|
|
4e9c1fbe96 | ||
|
|
406777d104 | ||
|
|
abe8f5e23f | ||
|
|
6a0e849067 | ||
|
|
a20c3f9c44 | ||
|
|
783aff17dd | ||
|
|
86ec3f37af | ||
|
|
e306a53999 | ||
|
|
90fb8cb08b | ||
|
|
5129c2f867 | ||
|
|
87d2d1d838 | ||
|
|
9aec8d9a47 | ||
|
|
c8f53d6690 | ||
|
|
875bcd4224 | ||
|
|
63dc71c57e | ||
|
|
3a200b75c9 | ||
|
|
745c8c17f1 | ||
|
|
5ecf3d78f1 | ||
|
|
b85a3b0d71 | ||
|
|
8777f61e7b | ||
|
|
31ede2432c | ||
|
|
c8fbf3ae34 | ||
|
|
3455dfc804 | ||
|
|
cd39cc1eec | ||
|
|
ccfa8d15bf | ||
|
|
799dab9dba |
@@ -26,6 +26,7 @@ env:
|
||||
- TESTENV=py27-nobyte
|
||||
- TESTENV=doctesting
|
||||
- TESTENV=freeze
|
||||
- TESTENV=docs
|
||||
|
||||
script: tox --recreate -e $TESTENV
|
||||
|
||||
|
||||
9
AUTHORS
9
AUTHORS
@@ -5,6 +5,7 @@ Contributors include::
|
||||
|
||||
Abdeali JK
|
||||
Abhijeet Kasurde
|
||||
Ahn Ki-Wook
|
||||
Alexei Kozlenok
|
||||
Anatoly Bubenkoff
|
||||
Andreas Zeidler
|
||||
@@ -48,7 +49,6 @@ Eduardo Schettino
|
||||
Elizaveta Shashkova
|
||||
Endre Galaczi
|
||||
Eric Hunsberger
|
||||
Eric Hunsberger
|
||||
Eric Siegerman
|
||||
Erik M. Bray
|
||||
Feng Ma
|
||||
@@ -70,6 +70,7 @@ Javier Domingo Cansino
|
||||
Javier Romero
|
||||
John Towler
|
||||
Jon Sonesen
|
||||
Jordan Guymon
|
||||
Joshua Bronson
|
||||
Jurko Gospodnetić
|
||||
Justyna Janczyszyn
|
||||
@@ -77,10 +78,12 @@ Kale Kundert
|
||||
Katarzyna Jachim
|
||||
Kevin Cox
|
||||
Lee Kamentsky
|
||||
Lev Maximov
|
||||
Lukas Bednar
|
||||
Maciek Fijalkowski
|
||||
Maho
|
||||
Marc Schlaich
|
||||
Marcin Bachry
|
||||
Mark Abramowitz
|
||||
Markus Unterwaditzer
|
||||
Martijn Faassen
|
||||
@@ -88,6 +91,8 @@ Martin K. Scherer
|
||||
Martin Prusse
|
||||
Matt Bachmann
|
||||
Matt Williams
|
||||
Matthias Hafner
|
||||
mbyt
|
||||
Michael Aquilina
|
||||
Michael Birtwell
|
||||
Michael Droettboom
|
||||
@@ -102,6 +107,7 @@ Punyashloka Biswal
|
||||
Quentin Pradet
|
||||
Ralf Schmitt
|
||||
Raphael Pierzina
|
||||
Raquel Alegre
|
||||
Roberto Polli
|
||||
Romain Dorgueil
|
||||
Roman Bolshakov
|
||||
@@ -121,6 +127,7 @@ Ted Xiao
|
||||
Thomas Grainger
|
||||
Tom Viner
|
||||
Trevor Bekolay
|
||||
Tyler Goodlet
|
||||
Vasily Kuznetsov
|
||||
Wouter van Ackooy
|
||||
Xuecong Liao
|
||||
|
||||
128
CHANGELOG.rst
128
CHANGELOG.rst
@@ -1,3 +1,107 @@
|
||||
3.0.3
|
||||
=====
|
||||
|
||||
* The ``ids`` argument to ``parametrize`` again accepts ``unicode`` strings
|
||||
in Python 2 (`#1905`_).
|
||||
Thanks `@philpep`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Assertions are now being rewritten for plugins in development mode
|
||||
(``pip install -e``) (`#1934`_).
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix pkg_resources import error in Jython projects (`#1853`).
|
||||
Thanks `@raquel-ucl`_ for the PR.
|
||||
|
||||
* Got rid of ``AttributeError: 'Module' object has no attribute '_obj'`` exception
|
||||
in Python 3 (`#1944`_).
|
||||
Thanks `@axil`_ for the PR.
|
||||
|
||||
* Explain a bad scope value passed to ``@fixture`` declarations or
|
||||
a ``MetaFunc.parametrize()`` call. Thanks `@tgoodlet`_ for the PR.
|
||||
|
||||
* This version includes ``pluggy-0.4.0``, which correctly handles
|
||||
``VersionConflict`` errors in plugins (`#704`_).
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
|
||||
.. _@philpep: https://github.com/philpep
|
||||
.. _@raquel-ucl: https://github.com/raquel-ucl
|
||||
.. _@axil: https://github.com/axil
|
||||
.. _@tgoodlet: https://github.com/tgoodlet
|
||||
|
||||
.. _#1905: https://github.com/pytest-dev/pytest/issues/1905
|
||||
.. _#1934: https://github.com/pytest-dev/pytest/issues/1934
|
||||
.. _#1944: https://github.com/pytest-dev/pytest/issues/1944
|
||||
.. _#704: https://github.com/pytest-dev/pytest/issues/704
|
||||
|
||||
|
||||
|
||||
3.0.2
|
||||
=====
|
||||
|
||||
* Improve error message when passing non-string ids to ``pytest.mark.parametrize`` (`#1857`_).
|
||||
Thanks `@okken`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Add ``buffer`` attribute to stdin stub class ``pytest.capture.DontReadFromInput``
|
||||
Thanks `@joguSD`_ for the PR.
|
||||
|
||||
* Fix ``UnicodeEncodeError`` when string comparison with unicode has failed. (`#1864`_)
|
||||
Thanks `@AiOO`_ for the PR.
|
||||
|
||||
* ``pytest_plugins`` is now handled correctly if defined as a string (as opposed as
|
||||
a sequence of strings) when modules are considered for assertion rewriting.
|
||||
Due to this bug, much more modules were being rewritten than necessary
|
||||
if a test suite uses ``pytest_plugins`` to load internal plugins (`#1888`_).
|
||||
Thanks `@jaraco`_ for the report and `@nicoddemus`_ for the PR (`#1891`_).
|
||||
|
||||
* Do not call tearDown and cleanups when running tests from
|
||||
``unittest.TestCase`` subclasses with ``--pdb``
|
||||
enabled. This allows proper post mortem debugging for all applications
|
||||
which have significant logic in their tearDown machinery (`#1890`_). Thanks
|
||||
`@mbyt`_ for the PR.
|
||||
|
||||
* Fix use of deprecated ``getfuncargvalue`` method in the internal doctest plugin.
|
||||
Thanks `@ViviCoder`_ for the report (`#1898`_).
|
||||
|
||||
.. _@joguSD: https://github.com/joguSD
|
||||
.. _@AiOO: https://github.com/AiOO
|
||||
.. _@mbyt: https://github.com/mbyt
|
||||
.. _@ViviCoder: https://github.com/ViviCoder
|
||||
|
||||
.. _#1857: https://github.com/pytest-dev/pytest/issues/1857
|
||||
.. _#1864: https://github.com/pytest-dev/pytest/issues/1864
|
||||
.. _#1888: https://github.com/pytest-dev/pytest/issues/1888
|
||||
.. _#1891: https://github.com/pytest-dev/pytest/pull/1891
|
||||
.. _#1890: https://github.com/pytest-dev/pytest/issues/1890
|
||||
.. _#1898: https://github.com/pytest-dev/pytest/issues/1898
|
||||
|
||||
|
||||
3.0.1
|
||||
=====
|
||||
|
||||
* Fix regression when ``importorskip`` is used at module level (`#1822`_).
|
||||
Thanks `@jaraco`_ and `@The-Compiler`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix parametrization scope when session fixtures are used in conjunction
|
||||
with normal parameters in the same call (`#1832`_).
|
||||
Thanks `@The-Compiler`_ for the report, `@Kingdread`_ and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix internal error when parametrizing tests or fixtures using an empty ``ids`` argument (`#1849`_).
|
||||
Thanks `@OPpuolitaival`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix loader error when running ``pytest`` embedded in a zipfile.
|
||||
Thanks `@mbachry`_ for the PR.
|
||||
|
||||
|
||||
.. _@Kingdread: https://github.com/Kingdread
|
||||
.. _@mbachry: https://github.com/mbachry
|
||||
.. _@OPpuolitaival: https://github.com/OPpuolitaival
|
||||
|
||||
.. _#1822: https://github.com/pytest-dev/pytest/issues/1822
|
||||
.. _#1832: https://github.com/pytest-dev/pytest/issues/1832
|
||||
.. _#1849: https://github.com/pytest-dev/pytest/issues/1849
|
||||
|
||||
|
||||
3.0.0
|
||||
=====
|
||||
|
||||
@@ -9,7 +113,11 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||
|
||||
* Reinterpretation mode has now been removed. Only plain and rewrite
|
||||
mode are available, consequently the ``--assert=reinterp`` option is
|
||||
no longer available. Thanks `@flub`_ for the PR.
|
||||
no longer available. This also means files imported from plugins or
|
||||
``conftest.py`` will not benefit from improved assertions by
|
||||
default, you should use ``pytest.register_assert_rewrite()`` to
|
||||
explicitly turn on assertion rewriting for those files. Thanks
|
||||
`@flub`_ for the PR.
|
||||
|
||||
* The following deprecated commandline options were removed:
|
||||
|
||||
@@ -234,12 +342,6 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||
removed in pytest-4.0 (`#1684`_).
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
* Raise helpful failure message, when requesting parametrized fixture at runtime,
|
||||
e.g. with ``request.getfuncargvalue``. BACKWARD INCOMPAT: Previously these params
|
||||
were simply never defined. So a fixture decorated like ``@pytest.fixture(params=[0, 1, 2])``
|
||||
only ran once. Now a failure is raised. Fixes (`#460`_). Thanks to
|
||||
`@nikratio`_ for bug report, `@RedBeardCode`_ and `@tomviner`_ for the PR.
|
||||
|
||||
* Passing a command-line string to ``pytest.main()`` is considered deprecated and scheduled
|
||||
for removal in pytest-4.0. It is recommended to pass a list of arguments instead (`#1723`_).
|
||||
|
||||
@@ -254,15 +356,14 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||
|
||||
* Refined logic for determining the ``rootdir``, considering only valid
|
||||
paths which fixes a number of issues: `#1594`_, `#1435`_ and `#1471`_.
|
||||
Thanks to `@blueyed`_ and `@davehunt`_ for the PR.
|
||||
Updated the documentation according to current behavior. Thanks to
|
||||
`@blueyed`_, `@davehunt`_ and `@matthiasha`_ for the PR.
|
||||
|
||||
* Always include full assertion explanation. The previous behaviour was hiding
|
||||
sub-expressions that happened to be False, assuming this was redundant information.
|
||||
Thanks `@bagerard`_ for reporting (`#1503`_). Thanks to `@davehunt`_ and
|
||||
`@tomviner`_ for PR.
|
||||
|
||||
* Renamed the pytest ``pdb`` module (plugin) into ``debugging``.
|
||||
|
||||
* Better message in case of not using parametrized variable (see `#1539`_).
|
||||
Thanks to `@tramwaj29`_ for the PR.
|
||||
|
||||
@@ -307,10 +408,6 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||
identify bugs in ``conftest.py`` files (`#1516`_). Thanks `@txomon`_ for
|
||||
the PR.
|
||||
|
||||
* Add an 'E' to the first line of error messages from FixtureLookupErrorRepr.
|
||||
Fixes `#717`_. Thanks `@blueyed`_ for reporting, `@eolo999`_ for the PR
|
||||
and `@tomviner`_ for his guidance during EuroPython2016 sprint.
|
||||
|
||||
* Text documents without any doctests no longer appear as "skipped".
|
||||
Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_).
|
||||
|
||||
@@ -428,6 +525,7 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||
.. _@tramwaj29: https://github.com/tramwaj29
|
||||
.. _@txomon: https://github.com/txomon
|
||||
.. _@Vogtinator: https://github.com/Vogtinator
|
||||
.. _@matthiasha: https://github.com/matthiasha
|
||||
|
||||
|
||||
2.9.2
|
||||
@@ -1208,7 +1306,7 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||
dep). Thanks Charles Cloud for analysing the issue.
|
||||
|
||||
- fix conftest related fixture visibility issue: when running with a
|
||||
CWD outside 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.
|
||||
|
||||
- Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the
|
||||
|
||||
113
HOWTORELEASE.rst
113
HOWTORELEASE.rst
@@ -3,90 +3,83 @@ How to release pytest
|
||||
|
||||
Note: this assumes you have already registered on pypi.
|
||||
|
||||
0. create the branch release-VERSION
|
||||
use features as base for minor/major releases
|
||||
and master as base for bugfix releases
|
||||
1. Bump version numbers in ``_pytest/__init__.py`` (``setup.py`` reads it).
|
||||
|
||||
1. Bump version numbers in _pytest/__init__.py (setup.py reads it)
|
||||
2. Check and finalize ``CHANGELOG.rst``.
|
||||
|
||||
2. Check and finalize CHANGELOG
|
||||
3. Write ``doc/en/announce/release-VERSION.txt`` and include
|
||||
it in ``doc/en/announce/index.txt``. Run this command to list names of authors involved::
|
||||
|
||||
3. Write doc/en/announce/release-VERSION.txt and include
|
||||
it in doc/en/announce/index.txt::
|
||||
git log $(git describe --abbrev=0 --tags)..HEAD --format='%aN' | sort -u
|
||||
|
||||
git log 2.8.2..HEAD --format='%aN' | sort -u # lists the names of authors involved
|
||||
4. Regenerate the docs examples using tox::
|
||||
|
||||
4. Use devpi for uploading a release tarball to a staging area::
|
||||
tox -e regen
|
||||
|
||||
5. At this point, open a PR named ``release-X`` so others can help find regressions or provide suggestions.
|
||||
|
||||
6. Use devpi for uploading a release tarball to a staging area::
|
||||
|
||||
devpi use https://devpi.net/USER/dev
|
||||
devpi upload --formats sdist,bdist_wheel
|
||||
|
||||
5. Run from multiple machines::
|
||||
7. Run from multiple machines::
|
||||
|
||||
devpi use https://devpi.net/USER/dev
|
||||
devpi test pytest==VERSION
|
||||
|
||||
6. Check that tests pass for relevant combinations with::
|
||||
Alternatively, you can use `devpi-cloud-tester <https://github.com/nicoddemus/devpi-cloud-tester>`_ to test
|
||||
the package on AppVeyor and Travis (follow instructions on the ``README``).
|
||||
|
||||
8. Check that tests pass for relevant combinations with::
|
||||
|
||||
devpi list pytest
|
||||
|
||||
or look at failures with "devpi list -f pytest".
|
||||
|
||||
7. Regenerate the docs examples using tox, and check for regressions::
|
||||
|
||||
tox -e regen
|
||||
git diff
|
||||
|
||||
|
||||
8. Build the docs, you need a virtualenv with py and sphinx
|
||||
installed::
|
||||
|
||||
cd doc/en
|
||||
make html
|
||||
|
||||
Commit any changes before tagging the release.
|
||||
|
||||
9. Tag the release::
|
||||
|
||||
git tag VERSION
|
||||
git push
|
||||
|
||||
10. Upload the docs using doc/en/Makefile::
|
||||
|
||||
cd doc/en
|
||||
make install # or "installall" if you have LaTeX installed for PDF
|
||||
|
||||
This requires ssh-login permission on pytest.org because it uses
|
||||
rsync.
|
||||
Note that the ``install`` target of ``doc/en/Makefile`` defines where the
|
||||
rsync goes to, typically to the "latest" section of pytest.org.
|
||||
|
||||
If you are making a minor release (e.g. 5.4), you also need to manually
|
||||
create a symlink for "latest"::
|
||||
|
||||
ssh pytest-dev@pytest.org
|
||||
ln -s 5.4 latest
|
||||
|
||||
Browse to pytest.org to verify.
|
||||
|
||||
11. Publish to pypi::
|
||||
9. Feeling confident? Publish to pypi::
|
||||
|
||||
devpi push pytest==VERSION pypi:NAME
|
||||
|
||||
where NAME is the name of pypi.python.org as configured in your ``~/.pypirc``
|
||||
file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_.
|
||||
where NAME is the name of pypi.python.org as configured in your ``~/.pypirc``
|
||||
file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_.
|
||||
|
||||
10. Tag the release::
|
||||
|
||||
12. Send release announcement to mailing lists:
|
||||
git tag VERSION <hash>
|
||||
git push origin VERSION
|
||||
|
||||
- pytest-dev
|
||||
- testing-in-python
|
||||
Make sure ``<hash>`` is **exactly** the git hash at the time the package was created.
|
||||
|
||||
11. Send release announcement to mailing lists:
|
||||
|
||||
- pytest-dev@python.org
|
||||
- python-announce-list@python.org
|
||||
- testing-in-python@lists.idyll.org (only for minor/major releases)
|
||||
|
||||
And announce the release on Twitter, making sure to add the hashtag ``#pytest``.
|
||||
|
||||
12. **After the release**
|
||||
|
||||
a. **patch release (2.8.3)**:
|
||||
|
||||
1. Checkout ``master``.
|
||||
2. Update version number in ``_pytest/__init__.py`` to ``"2.8.4.dev0"``.
|
||||
3. Create a new section in ``CHANGELOG.rst`` titled ``2.8.4.dev0`` and add a few bullet points as placeholders for new entries.
|
||||
4. Commit and push.
|
||||
|
||||
b. **minor release (2.9.0)**:
|
||||
|
||||
1. Merge ``features`` into ``master``.
|
||||
2. Checkout ``master``.
|
||||
3. Follow the same steps for a **patch release** above, using the next patch release: ``2.9.1.dev0``.
|
||||
4. Commit ``master``.
|
||||
5. Checkout ``features`` and merge with ``master`` (should be a fast-forward at this point).
|
||||
6. Update version number in ``_pytest/__init__.py`` to the next minor release: ``"2.10.0.dev0"``.
|
||||
7. Create a new section in ``CHANGELOG.rst`` titled ``2.10.0.dev0``, above ``2.9.1.dev0``, and add a few bullet points as placeholders for new entries.
|
||||
8. Commit ``features``.
|
||||
9. Push ``master`` and ``features``.
|
||||
|
||||
c. **major release (3.0.0)**: same steps as that of a **minor release**
|
||||
|
||||
|
||||
13. **after the release** Bump the version number in ``_pytest/__init__.py``,
|
||||
to the next Minor release version (i.e. if you released ``pytest-2.8.0``,
|
||||
set it to ``pytest-2.9.0.dev1``).
|
||||
|
||||
14. merge the actual release into the master branch and do a pull request against it
|
||||
15. merge from master to features
|
||||
|
||||
365
ISSUES.txt
365
ISSUES.txt
@@ -1,365 +0,0 @@
|
||||
|
||||
|
||||
recorder = monkeypatch.function(".......")
|
||||
-------------------------------------------------------------
|
||||
tags: nice feature
|
||||
|
||||
Like monkeypatch.replace but sets a mock-like call recorder:
|
||||
|
||||
recorder = monkeypatch.function("os.path.abspath")
|
||||
recorder.set_return("/hello")
|
||||
os.path.abspath("hello")
|
||||
call, = recorder.calls
|
||||
assert call.args.path == "hello"
|
||||
assert call.returned == "/hello"
|
||||
...
|
||||
|
||||
Unlike mock, "args.path" acts on the parsed auto-spec'ed ``os.path.abspath``
|
||||
so it's independent from if the client side called "os.path.abspath(path=...)"
|
||||
or "os.path.abspath('positional')".
|
||||
|
||||
|
||||
refine parametrize API
|
||||
-------------------------------------------------------------
|
||||
tags: critical feature
|
||||
|
||||
extend metafunc.parametrize to directly support indirection, example:
|
||||
|
||||
def setupdb(request, config):
|
||||
# setup "resource" based on test request and the values passed
|
||||
# in to parametrize. setupfunc is called for each such value.
|
||||
# you may use request.addfinalizer() or request.cached_setup ...
|
||||
return dynamic_setup_database(val)
|
||||
|
||||
@pytest.mark.parametrize("db", ["pg", "mysql"], setupfunc=setupdb)
|
||||
def test_heavy_functional_test(db):
|
||||
...
|
||||
|
||||
There would be no need to write or explain funcarg factories and
|
||||
their special __ syntax.
|
||||
|
||||
The examples and improvements should also show how to put the parametrize
|
||||
decorator to a class, to a module or even to a directory. For the directory
|
||||
part a conftest.py content like this::
|
||||
|
||||
pytestmark = [
|
||||
@pytest.mark.parametrize_setup("db", ...),
|
||||
]
|
||||
|
||||
probably makes sense in order to keep the declarative nature. This mirrors
|
||||
the marker-mechanism with respect to a test module but puts it to a directory
|
||||
scale.
|
||||
|
||||
When doing larger scoped parametrization it probably becomes necessary
|
||||
to allow parametrization to be ignored if the according parameter is not
|
||||
used (currently any parametrized argument that is not present in a function will cause a ValueError). Example:
|
||||
|
||||
@pytest.mark.parametrize("db", ..., mustmatch=False)
|
||||
|
||||
means to not raise an error but simply ignore the parametrization
|
||||
if the signature of a decorated function does not match. XXX is it
|
||||
not sufficient to always allow non-matches?
|
||||
|
||||
|
||||
allow parametrized attributes on classes
|
||||
--------------------------------------------------
|
||||
|
||||
tags: wish 2.4
|
||||
|
||||
example:
|
||||
|
||||
@pytest.mark.parametrize_attr("db", setupfunc, [1,2,3], scope="class")
|
||||
@pytest.mark.parametrize_attr("tmp", setupfunc, scope="...")
|
||||
class TestMe:
|
||||
def test_hello(self):
|
||||
access self.db ...
|
||||
|
||||
this would run the test_hello() function three times with three
|
||||
different values for self.db. This could also work with unittest/nose
|
||||
style tests, i.e. it leverages existing test suites without needing
|
||||
to rewrite them. Together with the previously mentioned setup_test()
|
||||
maybe the setupfunc could be omitted?
|
||||
|
||||
optimizations
|
||||
---------------------------------------------------------------
|
||||
tags: 2.4 core
|
||||
|
||||
- look at ihook optimization such that all lookups for
|
||||
hooks relating to the same fspath are cached.
|
||||
|
||||
fix start/finish partial finailization problem
|
||||
---------------------------------------------------------------
|
||||
tags: bug core
|
||||
|
||||
if a configure/runtest_setup/sessionstart/... hook invocation partially
|
||||
fails the sessionfinishes is not called. Each hook implementation
|
||||
should better be repsonsible for registering a cleanup/finalizer
|
||||
appropriately to avoid this issue. Moreover/Alternatively, we could
|
||||
record which implementations of a hook succeeded and only call their
|
||||
teardown.
|
||||
|
||||
|
||||
relax requirement to have tests/testing contain an __init__
|
||||
----------------------------------------------------------------
|
||||
tags: feature
|
||||
bb: http://bitbucket.org/hpk42/py-trunk/issue/64
|
||||
|
||||
A local test run of a "tests" directory may work
|
||||
but a remote one fail because the tests directory
|
||||
does not contain an "__init__.py". Either give
|
||||
an error or make it work without the __init__.py
|
||||
i.e. port the nose-logic of unloading a test module.
|
||||
|
||||
customize test function collection
|
||||
-------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
- introduce pytest.mark.nocollect for not considering a function for
|
||||
test collection at all. maybe also introduce a pytest.mark.test to
|
||||
explicitly mark a function to become a tested one. Lookup JUnit ways
|
||||
of tagging tests.
|
||||
|
||||
introduce pytest.mark.importorskip
|
||||
-------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
in addition to the imperative pytest.importorskip also introduce
|
||||
a pytest.mark.importorskip so that the test count is more correct.
|
||||
|
||||
|
||||
introduce pytest.mark.platform
|
||||
-------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
Introduce nice-to-spell platform-skipping, examples:
|
||||
|
||||
@pytest.mark.platform("python3")
|
||||
@pytest.mark.platform("not python3")
|
||||
@pytest.mark.platform("win32 and not python3")
|
||||
@pytest.mark.platform("darwin")
|
||||
@pytest.mark.platform("not (jython and win32)")
|
||||
@pytest.mark.platform("not (jython and win32)", xfail=True)
|
||||
|
||||
etc. Idea is to allow Python expressions which can operate
|
||||
on common spellings for operating systems and python
|
||||
interpreter versions.
|
||||
|
||||
pytest.mark.xfail signature change
|
||||
-------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
change to pytest.mark.xfail(reason, (optional)condition)
|
||||
to better implement the word meaning. It also signals
|
||||
better that we always have some kind of an implementation
|
||||
reason that can be formualated.
|
||||
Compatibility? how to introduce a new name/keep compat?
|
||||
|
||||
allow to non-intrusively apply skipfs/xfail/marks
|
||||
---------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
use case: mark a module or directory structures
|
||||
to be skipped on certain platforms (i.e. no import
|
||||
attempt will be made).
|
||||
|
||||
consider introducing a hook/mechanism that allows to apply marks
|
||||
from conftests or plugins. (See extended parametrization)
|
||||
|
||||
|
||||
explicit referencing of conftest.py files
|
||||
-----------------------------------------
|
||||
tags: feature
|
||||
|
||||
allow to name conftest.py files (in sub directories) that should
|
||||
be imported early, as to include command line options.
|
||||
|
||||
improve central pytest ini file
|
||||
-------------------------------
|
||||
tags: feature
|
||||
|
||||
introduce more declarative configuration options:
|
||||
- (to-be-collected test directories)
|
||||
- required plugins
|
||||
- test func/class/file matching patterns
|
||||
- skip/xfail (non-intrusive)
|
||||
- pytest.ini and tox.ini and setup.cfg configuration in the same file
|
||||
|
||||
new documentation
|
||||
----------------------------------
|
||||
tags: feature
|
||||
|
||||
- logo pytest
|
||||
- examples for unittest or functional testing
|
||||
- resource management for functional testing
|
||||
- patterns: page object
|
||||
|
||||
have imported module mismatch honour relative paths
|
||||
--------------------------------------------------------
|
||||
tags: bug
|
||||
|
||||
With 1.1.1 pytest fails at least on windows if an import
|
||||
is relative and compared against an absolute conftest.py
|
||||
path. Normalize.
|
||||
|
||||
consider globals: pytest.ensuretemp and config
|
||||
--------------------------------------------------------------
|
||||
tags: experimental-wish
|
||||
|
||||
consider deprecating pytest.ensuretemp and pytest.config
|
||||
to further reduce pytest globality. Also consider
|
||||
having pytest.config and ensuretemp coming from
|
||||
a plugin rather than being there from the start.
|
||||
|
||||
|
||||
consider pytest_addsyspath hook
|
||||
-----------------------------------------
|
||||
tags: wish
|
||||
|
||||
pytest could call a new pytest_addsyspath() in order to systematically
|
||||
allow manipulation of sys.path and to inhibit it via --no-addsyspath
|
||||
in order to more easily run against installed packages.
|
||||
|
||||
Alternatively it could also be done via the config object
|
||||
and pytest_configure.
|
||||
|
||||
|
||||
|
||||
deprecate global pytest.config usage
|
||||
----------------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
pytest.ensuretemp and pytest.config are probably the last
|
||||
objects containing global state. Often using them is not
|
||||
necessary. This is about trying to get rid of them, i.e.
|
||||
deprecating them and checking with PyPy's usages as well
|
||||
as others.
|
||||
|
||||
remove deprecated bits in collect.py
|
||||
-------------------------------------------------------------------
|
||||
tags: feature
|
||||
|
||||
In an effort to further simplify code, review and remove deprecated bits
|
||||
in collect.py. Probably good:
|
||||
- inline consider_file/dir methods, no need to have them
|
||||
subclass-overridable because of hooks
|
||||
|
||||
implement fslayout decorator
|
||||
---------------------------------
|
||||
tags: feature
|
||||
|
||||
Improve the way how tests can work with pre-made examples,
|
||||
keeping the layout close to the test function:
|
||||
|
||||
@pytest.mark.fslayout("""
|
||||
conftest.py:
|
||||
# empty
|
||||
tests/
|
||||
test_%(NAME)s: # becomes test_run1.py
|
||||
def test_function(self):
|
||||
pass
|
||||
""")
|
||||
def test_run(pytester, fslayout):
|
||||
p = fslayout.findone("test_*.py")
|
||||
result = pytester.runpytest(p)
|
||||
assert result.ret == 0
|
||||
assert result.passed == 1
|
||||
|
||||
Another idea is to allow to define a full scenario including the run
|
||||
in one content string::
|
||||
|
||||
runscenario("""
|
||||
test_{TESTNAME}.py:
|
||||
import pytest
|
||||
@pytest.mark.xfail
|
||||
def test_that_fails():
|
||||
assert 0
|
||||
|
||||
@pytest.mark.skipif("True")
|
||||
def test_hello():
|
||||
pass
|
||||
|
||||
conftest.py:
|
||||
import pytest
|
||||
def pytest_runsetup_setup(item):
|
||||
pytest.skip("abc")
|
||||
|
||||
runpytest -rsxX
|
||||
*SKIP*{TESTNAME}*
|
||||
*1 skipped*
|
||||
""")
|
||||
|
||||
This could be run with at least three different ways to invoke pytest:
|
||||
through the shell, through "python -m pytest" and inlined. As inlined
|
||||
would be the fastest it could be run first (or "--fast" mode).
|
||||
|
||||
|
||||
Create isolate plugin
|
||||
---------------------
|
||||
tags: feature
|
||||
|
||||
The idea is that you can e.g. import modules in a test and afterwards
|
||||
sys.modules, sys.meta_path etc would be reverted. It can go further
|
||||
then just importing however, e.g. current working directory, file
|
||||
descriptors, ...
|
||||
|
||||
This would probably be done by marking::
|
||||
|
||||
@pytest.mark.isolate(importing=True, cwd=True, fds=False)
|
||||
def test_foo():
|
||||
...
|
||||
|
||||
With the possibility of doing this globally in an ini-file.
|
||||
|
||||
|
||||
fnmatch for test names
|
||||
----------------------
|
||||
tags: feature-wish
|
||||
|
||||
various testsuites use suffixes instead of prefixes for test classes
|
||||
also it lends itself to bdd style test names::
|
||||
|
||||
class UserBehaviour:
|
||||
def anonymous_should_not_have_inbox(user):
|
||||
...
|
||||
def registred_should_have_inbox(user):
|
||||
..
|
||||
|
||||
using the following in pytest.ini::
|
||||
|
||||
[pytest]
|
||||
python_classes = Test *Behaviour *Test
|
||||
python_functions = test *_should_*
|
||||
|
||||
|
||||
mechanism for running named parts of tests with different reporting behaviour
|
||||
------------------------------------------------------------------------------
|
||||
tags: feature-wish-incomplete
|
||||
|
||||
a few use-cases come to mind:
|
||||
|
||||
* fail assertions and record that without stopping a complete test
|
||||
|
||||
* this is in particular hepfull if a small bit of a test is known to fail/xfail::
|
||||
|
||||
def test_fun():
|
||||
with pytest.section('fdcheck, marks=pytest.mark.xfail_if(...)):
|
||||
breaks_on_windows()
|
||||
|
||||
* divide functional/acceptance tests into sections
|
||||
* provide a different mechanism for generators, maybe something like::
|
||||
|
||||
def pytest_runtest_call(item)
|
||||
if not generator:
|
||||
...
|
||||
prepare_check = GeneratorCheckprepare()
|
||||
|
||||
gen = item.obj(**fixtures)
|
||||
for check in gen
|
||||
id, call = prepare_check(check)
|
||||
# bubble should only prevent exception propagation after a failure
|
||||
# the whole test should still fail
|
||||
# there might be need for a lower level api and taking custom markers into account
|
||||
with pytest.section(id, bubble=False):
|
||||
call()
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ include AUTHORS
|
||||
|
||||
include README.rst
|
||||
include CONTRIBUTING.rst
|
||||
include HOWTORELEASE.rst
|
||||
|
||||
include tox.ini
|
||||
include setup.py
|
||||
@@ -29,6 +30,3 @@ recursive-exclude * *.pyc *.pyo
|
||||
exclude appveyor/install.ps1
|
||||
exclude appveyor.yml
|
||||
exclude appveyor
|
||||
|
||||
exclude ISSUES.txt
|
||||
exclude HOWTORELEASE.rst
|
||||
|
||||
@@ -35,7 +35,6 @@ To execute it::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
|
||||
collected 1 items
|
||||
|
||||
test_sample.py F
|
||||
@@ -52,7 +51,7 @@ To execute it::
|
||||
======= 1 failed in 0.12 seconds ========
|
||||
|
||||
|
||||
Due to ``py.test``'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.
|
||||
|
||||
|
||||
Features
|
||||
@@ -64,7 +63,7 @@ Features
|
||||
<http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
|
||||
of test modules and functions;
|
||||
|
||||
- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for
|
||||
- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for
|
||||
managing small or parametrized long-lived test resources;
|
||||
|
||||
- Can run `unittest <http://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#
|
||||
__version__ = '3.0.0'
|
||||
__version__ = '3.0.3'
|
||||
|
||||
@@ -12,6 +12,7 @@ if sys.version_info[0] >= 3:
|
||||
else:
|
||||
from ._py2traceback import format_exception_only
|
||||
|
||||
|
||||
class Code(object):
|
||||
""" wrapper around Python code objects """
|
||||
def __init__(self, rawcode):
|
||||
@@ -28,6 +29,8 @@ class Code(object):
|
||||
def __eq__(self, other):
|
||||
return self.raw == other.raw
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
@@ -354,7 +357,7 @@ class ExceptionInfo(object):
|
||||
if exprinfo is None and isinstance(tup[1], AssertionError):
|
||||
exprinfo = getattr(tup[1], 'msg', None)
|
||||
if exprinfo is None:
|
||||
exprinfo = str(tup[1])
|
||||
exprinfo = py._builtin._totext(tup[1])
|
||||
if exprinfo and exprinfo.startswith('assert '):
|
||||
self._striptext = 'AssertionError: '
|
||||
self._excinfo = tup
|
||||
|
||||
@@ -52,22 +52,21 @@ class Source(object):
|
||||
return str(self) == other
|
||||
return False
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __getitem__(self, key):
|
||||
if isinstance(key, int):
|
||||
return self.lines[key]
|
||||
else:
|
||||
if key.step not in (None, 1):
|
||||
raise IndexError("cannot slice a Source with a step")
|
||||
return self.__getslice__(key.start, key.stop)
|
||||
newsource = Source()
|
||||
newsource.lines = self.lines[key.start:key.stop]
|
||||
return newsource
|
||||
|
||||
def __len__(self):
|
||||
return len(self.lines)
|
||||
|
||||
def __getslice__(self, start, end):
|
||||
newsource = Source()
|
||||
newsource.lines = self.lines[start:end]
|
||||
return newsource
|
||||
|
||||
def strip(self):
|
||||
""" return new source object with trailing
|
||||
and leading blank lines removed.
|
||||
|
||||
@@ -36,7 +36,13 @@ def register_assert_rewrite(*names):
|
||||
Thus you should make sure to call this before the module is
|
||||
actually imported, usually in your __init__.py if you are a plugin
|
||||
using a package.
|
||||
|
||||
:raise TypeError: if the given module names are not strings.
|
||||
"""
|
||||
for name in names:
|
||||
if not isinstance(name, str):
|
||||
msg = 'expected module names as *args, got {0} instead'
|
||||
raise TypeError(msg.format(repr(names)))
|
||||
for hook in sys.meta_path:
|
||||
if isinstance(hook, rewrite.AssertionRewritingHook):
|
||||
importhook = hook
|
||||
|
||||
@@ -105,7 +105,7 @@ except NameError:
|
||||
def assertrepr_compare(config, op, left, right):
|
||||
"""Return specialised explanations for some operators/operands"""
|
||||
width = 80 - 15 - len(op) - 2 # 15 chars indentation, 1 space around op
|
||||
left_repr = py.io.saferepr(left, maxsize=int(width/2))
|
||||
left_repr = py.io.saferepr(left, maxsize=int(width//2))
|
||||
right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
|
||||
|
||||
summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr))
|
||||
|
||||
@@ -455,6 +455,13 @@ class DontReadFromInput:
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def buffer(self):
|
||||
if sys.version_info >= (3,0):
|
||||
return self
|
||||
else:
|
||||
raise AttributeError('redirected stdin has no attribute buffer')
|
||||
|
||||
|
||||
def _readline_workaround():
|
||||
"""
|
||||
|
||||
@@ -5,7 +5,6 @@ import traceback
|
||||
import types
|
||||
import warnings
|
||||
|
||||
import pkg_resources
|
||||
import py
|
||||
# DON't import pytest here because it causes import cycle troubles
|
||||
import sys, os
|
||||
@@ -379,6 +378,8 @@ class PytestPluginManager(PluginManager):
|
||||
|
||||
def consider_module(self, mod):
|
||||
plugins = getattr(mod, 'pytest_plugins', [])
|
||||
if isinstance(plugins, str):
|
||||
plugins = [plugins]
|
||||
self.rewrite_hook.mark_rewrite(*plugins)
|
||||
self._import_plugin_specs(plugins)
|
||||
|
||||
@@ -950,18 +951,24 @@ class Config(object):
|
||||
except SystemError:
|
||||
mode = 'plain'
|
||||
else:
|
||||
import pkg_resources
|
||||
self.pluginmanager.rewrite_hook = hook
|
||||
for entrypoint in pkg_resources.iter_entry_points('pytest11'):
|
||||
for entry in entrypoint.dist._get_metadata('RECORD'):
|
||||
fn = entry.split(',')[0]
|
||||
is_simple_module = os.sep not in fn and fn.endswith('.py')
|
||||
is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
|
||||
if is_simple_module:
|
||||
module_name, ext = os.path.splitext(fn)
|
||||
hook.mark_rewrite(module_name)
|
||||
elif is_package:
|
||||
package_name = os.path.dirname(fn)
|
||||
hook.mark_rewrite(package_name)
|
||||
# 'RECORD' available for plugins installed normally (pip install)
|
||||
# 'SOURCES.txt' available for plugins installed in dev mode (pip install -e)
|
||||
# for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa
|
||||
# so it shouldn't be an issue
|
||||
for metadata in ('RECORD', 'SOURCES.txt'):
|
||||
for entry in entrypoint.dist._get_metadata(metadata):
|
||||
fn = entry.split(',')[0]
|
||||
is_simple_module = os.sep not in fn and fn.endswith('.py')
|
||||
is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
|
||||
if is_simple_module:
|
||||
module_name, ext = os.path.splitext(fn)
|
||||
hook.mark_rewrite(module_name)
|
||||
elif is_package:
|
||||
package_name = os.path.dirname(fn)
|
||||
hook.mark_rewrite(package_name)
|
||||
self._warn_about_missing_assertion(mode)
|
||||
|
||||
def _warn_about_missing_assertion(self, mode):
|
||||
|
||||
@@ -14,7 +14,7 @@ def pytest_addoption(parser):
|
||||
group._addoption(
|
||||
'--pdbcls', dest="usepdb_cls", metavar="modulename:classname",
|
||||
help="start a custom interactive Python debugger on errors. "
|
||||
"For example: --pdbcls=IPython.core.debugger:Pdb")
|
||||
"For example: --pdbcls=IPython.terminal.debugger:TerminalPdb")
|
||||
|
||||
def pytest_namespace():
|
||||
return {'set_trace': pytestPDB().set_trace}
|
||||
|
||||
@@ -88,7 +88,7 @@ class DoctestItem(pytest.Item):
|
||||
if self.dtest is not None:
|
||||
self.fixture_request = _setup_fixtures(self)
|
||||
globs = dict(getfixture=self.fixture_request.getfixturevalue)
|
||||
for name, value in self.fixture_request.getfuncargvalue('doctest_namespace').items():
|
||||
for name, value in self.fixture_request.getfixturevalue('doctest_namespace').items():
|
||||
globs[name] = value
|
||||
self.dtest.globs.update(globs)
|
||||
|
||||
|
||||
@@ -599,12 +599,29 @@ class ScopeMismatchError(Exception):
|
||||
which has a lower scope (e.g. a Session one calls a function one)
|
||||
"""
|
||||
|
||||
|
||||
scopes = "session module class function".split()
|
||||
scopenum_function = scopes.index("function")
|
||||
|
||||
|
||||
def scopemismatch(currentscope, newscope):
|
||||
return scopes.index(newscope) > scopes.index(currentscope)
|
||||
|
||||
|
||||
def scope2index(scope, descr, where=None):
|
||||
"""Look up the index of ``scope`` and raise a descriptive value error
|
||||
if not defined.
|
||||
"""
|
||||
try:
|
||||
return scopes.index(scope)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
"{0} {1}has an unsupported scope value '{2}'".format(
|
||||
descr, 'from {0} '.format(where) if where else '',
|
||||
scope)
|
||||
)
|
||||
|
||||
|
||||
class FixtureLookupError(LookupError):
|
||||
""" could not return a requested Fixture (missing or invalid). """
|
||||
def __init__(self, argname, request, msg=None):
|
||||
@@ -703,6 +720,7 @@ def call_fixture_func(fixturefunc, request, kwargs):
|
||||
res = fixturefunc(**kwargs)
|
||||
return res
|
||||
|
||||
|
||||
class FixtureDef:
|
||||
""" A container for a factory definition. """
|
||||
def __init__(self, fixturemanager, baseid, argname, func, scope, params,
|
||||
@@ -713,7 +731,11 @@ class FixtureDef:
|
||||
self.func = func
|
||||
self.argname = argname
|
||||
self.scope = scope
|
||||
self.scopenum = scopes.index(scope or "function")
|
||||
self.scopenum = scope2index(
|
||||
scope or "function",
|
||||
descr='fixture {0}'.format(func.__name__),
|
||||
where=baseid
|
||||
)
|
||||
self.params = params
|
||||
startindex = unittest and 1 or None
|
||||
self.argnames = getfuncargnames(func, startindex=startindex)
|
||||
|
||||
@@ -687,7 +687,7 @@ class Session(FSCollector):
|
||||
# This method is sometimes invoked when AssertionRewritingHook, which
|
||||
# does not define a get_filename method, is already in place:
|
||||
try:
|
||||
path = loader.get_filename()
|
||||
path = loader.get_filename(x)
|
||||
except AttributeError:
|
||||
# Retrieve path from AssertionRewritingHook:
|
||||
path = loader.modules[x][0].co_filename
|
||||
|
||||
@@ -19,7 +19,7 @@ def pytest_addoption(parser):
|
||||
help="only run tests which match the given substring expression. "
|
||||
"An expression is a python evaluatable expression "
|
||||
"where all names are substring-matched against test names "
|
||||
"and their parent classes. Example: -k 'test_method or test "
|
||||
"and their parent classes. Example: -k 'test_method or test_"
|
||||
"other' matches all test functions and classes whose name "
|
||||
"contains 'test_method' or 'test_other'. "
|
||||
"Additionally keywords are matched to classes and functions "
|
||||
@@ -283,6 +283,21 @@ class MarkDecorator:
|
||||
return self.__class__(self.name, args=args, kwargs=kw)
|
||||
|
||||
|
||||
def extract_argvalue(maybe_marked_args):
|
||||
# TODO: incorrect mark data, the old code wanst able to collect lists
|
||||
# individual parametrized argument sets can be wrapped in a series
|
||||
# of markers in which case we unwrap the values and apply the mark
|
||||
# at Function init
|
||||
newmarks = {}
|
||||
argval = maybe_marked_args
|
||||
while isinstance(argval, MarkDecorator):
|
||||
newmark = MarkDecorator(argval.markname,
|
||||
argval.args[:-1], argval.kwargs)
|
||||
newmarks[newmark.markname] = newmark
|
||||
argval = argval.args[-1]
|
||||
return argval, newmarks
|
||||
|
||||
|
||||
class MarkInfo:
|
||||
""" Marking object created by :class:`MarkDecorator` instances. """
|
||||
def __init__(self, name, args, kwargs):
|
||||
|
||||
@@ -5,10 +5,11 @@ import inspect
|
||||
import sys
|
||||
import collections
|
||||
import math
|
||||
from itertools import count
|
||||
|
||||
import py
|
||||
import pytest
|
||||
from _pytest.mark import MarkDecorator, MarkerError
|
||||
from _pytest.mark import MarkerError
|
||||
|
||||
|
||||
import _pytest
|
||||
@@ -204,11 +205,10 @@ class PyobjContext(object):
|
||||
class PyobjMixin(PyobjContext):
|
||||
def obj():
|
||||
def fget(self):
|
||||
try:
|
||||
return self._obj
|
||||
except AttributeError:
|
||||
obj = getattr(self, '_obj', None)
|
||||
if obj is None:
|
||||
self._obj = obj = self._getobj()
|
||||
return obj
|
||||
return obj
|
||||
def fset(self, value):
|
||||
self._obj = value
|
||||
return property(fget, fset, None, "underlying python object")
|
||||
@@ -431,10 +431,12 @@ class Module(pytest.File, PyCollector):
|
||||
"Make sure your test modules/packages have valid Python names."
|
||||
% (self.fspath, exc or exc_class)
|
||||
)
|
||||
except _pytest.runner.Skipped:
|
||||
except _pytest.runner.Skipped as e:
|
||||
if e.allow_module_level:
|
||||
raise
|
||||
raise self.CollectError(
|
||||
"Using @pytest.skip outside a test (e.g. as a test function "
|
||||
"decorator) is not allowed. Use @pytest.mark.skip or "
|
||||
"Using @pytest.skip outside of a test (e.g. as a test "
|
||||
"function decorator) is not allowed. Use @pytest.mark.skip or "
|
||||
"@pytest.mark.skipif instead."
|
||||
)
|
||||
self.config.pluginmanager.consider_module(mod)
|
||||
@@ -708,31 +710,26 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||
They help to inspect a test function and to generate tests according to
|
||||
test configuration or values specified in the class or module where a
|
||||
test function is defined.
|
||||
|
||||
:ivar fixturenames: set of fixture names required by the test function
|
||||
|
||||
:ivar function: underlying python test function
|
||||
|
||||
:ivar cls: class object where the test function is defined in or ``None``.
|
||||
|
||||
:ivar module: the module object where the test function is defined in.
|
||||
|
||||
:ivar config: access to the :class:`_pytest.config.Config` object for the
|
||||
test session.
|
||||
|
||||
:ivar funcargnames:
|
||||
.. deprecated:: 2.3
|
||||
Use ``fixturenames`` instead.
|
||||
"""
|
||||
def __init__(self, function, fixtureinfo, config, cls=None, module=None):
|
||||
#: access to the :class:`_pytest.config.Config` object for the test session
|
||||
self.config = config
|
||||
|
||||
#: the module object where the test function is defined in.
|
||||
self.module = module
|
||||
|
||||
#: underlying python test function
|
||||
self.function = function
|
||||
|
||||
#: set of fixture names required by the test function
|
||||
self.fixturenames = fixtureinfo.names_closure
|
||||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs
|
||||
|
||||
#: class object where the test function is defined in or ``None``.
|
||||
self.cls = cls
|
||||
|
||||
self._calls = []
|
||||
self._ids = py.builtin.set()
|
||||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs
|
||||
|
||||
def parametrize(self, argnames, argvalues, indirect=False, ids=None,
|
||||
scope=None):
|
||||
@@ -773,20 +770,16 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||
It will also override any fixture-function defined scope, allowing
|
||||
to set a dynamic scope using test context or configuration.
|
||||
"""
|
||||
from _pytest.fixtures import scopes
|
||||
# individual parametrized argument sets can be wrapped in a series
|
||||
# of markers in which case we unwrap the values and apply the mark
|
||||
# at Function init
|
||||
newkeywords = {}
|
||||
from _pytest.fixtures import scope2index
|
||||
from _pytest.mark import extract_argvalue
|
||||
from py.io import saferepr
|
||||
|
||||
unwrapped_argvalues = []
|
||||
for i, argval in enumerate(argvalues):
|
||||
while isinstance(argval, MarkDecorator):
|
||||
newmark = MarkDecorator(argval.markname,
|
||||
argval.args[:-1], argval.kwargs)
|
||||
newmarks = newkeywords.setdefault(i, {})
|
||||
newmarks[newmark.markname] = newmark
|
||||
argval = argval.args[-1]
|
||||
newkeywords = []
|
||||
for maybe_marked_args in argvalues:
|
||||
argval, newmarks = extract_argvalue(maybe_marked_args)
|
||||
unwrapped_argvalues.append(argval)
|
||||
newkeywords.append(newmarks)
|
||||
argvalues = unwrapped_argvalues
|
||||
|
||||
if not isinstance(argnames, (tuple, list)):
|
||||
@@ -801,19 +794,13 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||
newmark = pytest.mark.skip(
|
||||
reason="got empty parameter set %r, function %s at %s:%d" % (
|
||||
argnames, self.function.__name__, fs, lineno))
|
||||
newmarks = newkeywords.setdefault(0, {})
|
||||
newmarks[newmark.markname] = newmark
|
||||
newkeywords = [{newmark.markname: newmark}]
|
||||
|
||||
if scope is None:
|
||||
if self._arg2fixturedefs:
|
||||
# Takes the most narrow scope from used fixtures
|
||||
fixtures_scopes = [fixturedef[0].scope for fixturedef in self._arg2fixturedefs.values()]
|
||||
for scope in reversed(scopes):
|
||||
if scope in fixtures_scopes:
|
||||
break
|
||||
else:
|
||||
scope = 'function'
|
||||
scopenum = scopes.index(scope)
|
||||
scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
|
||||
|
||||
scopenum = scope2index(
|
||||
scope, descr='call to {0}'.format(self.parametrize))
|
||||
valtypes = {}
|
||||
for arg in argnames:
|
||||
if arg not in self.fixturenames:
|
||||
@@ -840,18 +827,23 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||
if callable(ids):
|
||||
idfn = ids
|
||||
ids = None
|
||||
if ids and len(ids) != len(argvalues):
|
||||
raise ValueError('%d tests specified with %d ids' %(
|
||||
len(argvalues), len(ids)))
|
||||
if ids:
|
||||
if len(ids) != len(argvalues):
|
||||
raise ValueError('%d tests specified with %d ids' %(
|
||||
len(argvalues), len(ids)))
|
||||
for id_value in ids:
|
||||
if id_value is not None and not isinstance(id_value, py.builtin._basestring):
|
||||
msg = 'ids must be list of strings, found: %s (type: %s)'
|
||||
raise ValueError(msg % (saferepr(id_value), type(id_value).__name__))
|
||||
ids = idmaker(argnames, argvalues, idfn, ids, self.config)
|
||||
newcalls = []
|
||||
for callspec in self._calls or [CallSpec2(self)]:
|
||||
for param_index, valset in enumerate(argvalues):
|
||||
elements = zip(ids, argvalues, newkeywords, count())
|
||||
for a_id, valset, keywords, param_index in elements:
|
||||
assert len(valset) == len(argnames)
|
||||
newcallspec = callspec.copy(self)
|
||||
newcallspec.setmulti(valtypes, argnames, valset, ids[param_index],
|
||||
newkeywords.get(param_index, {}), scopenum,
|
||||
param_index)
|
||||
newcallspec.setmulti(valtypes, argnames, valset, a_id,
|
||||
keywords, scopenum, param_index)
|
||||
newcalls.append(newcallspec)
|
||||
self._calls = newcalls
|
||||
|
||||
@@ -892,6 +884,30 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||
self._calls.append(cs)
|
||||
|
||||
|
||||
def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
|
||||
"""Find the most appropriate scope for a parametrized call based on its arguments.
|
||||
|
||||
When there's at least one direct argument, always use "function" scope.
|
||||
|
||||
When a test function is parametrized and all its arguments are indirect
|
||||
(e.g. fixtures), return the most narrow scope based on the fixtures used.
|
||||
|
||||
Related to issue #1832, based on code posted by @Kingdread.
|
||||
"""
|
||||
from _pytest.fixtures import scopes
|
||||
indirect_as_list = isinstance(indirect, (list, tuple))
|
||||
all_arguments_are_fixtures = indirect is True or \
|
||||
indirect_as_list and len(indirect) == argnames
|
||||
if all_arguments_are_fixtures:
|
||||
fixturedefs = arg2fixturedefs or {}
|
||||
used_scopes = [fixturedef[0].scope for name, fixturedef in fixturedefs.items()]
|
||||
if used_scopes:
|
||||
# Takes the most narrow scope from used fixtures
|
||||
for scope in reversed(scopes):
|
||||
if scope in used_scopes:
|
||||
return scope
|
||||
|
||||
return 'function'
|
||||
|
||||
|
||||
def _idval(val, argname, idx, idfn, config=None):
|
||||
@@ -921,7 +937,7 @@ def _idval(val, argname, idx, idfn, config=None):
|
||||
return str(argname)+str(idx)
|
||||
|
||||
def _idvalset(idx, valset, argnames, idfn, ids, config=None):
|
||||
if ids is None or ids[idx] is None:
|
||||
if ids is None or (idx >= len(ids) or ids[idx] is None):
|
||||
this_id = [_idval(val, argname, idx, idfn, config)
|
||||
for val, argname in zip(valset, argnames)]
|
||||
return "-".join(this_id)
|
||||
@@ -1341,6 +1357,8 @@ class approx(object):
|
||||
return False
|
||||
return all(a == x for a, x in zip(actual, self.expected))
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __ne__(self, actual):
|
||||
return not (actual == self)
|
||||
|
||||
@@ -1420,6 +1438,8 @@ class ApproxNonIterable(object):
|
||||
# Return true if the two numbers are within the tolerance.
|
||||
return abs(self.expected - actual) <= self.tolerance
|
||||
|
||||
__hash__ = None
|
||||
|
||||
def __ne__(self, actual):
|
||||
return not (actual == self)
|
||||
|
||||
|
||||
@@ -492,10 +492,16 @@ class Skipped(OutcomeException):
|
||||
# in order to have Skipped exception printing shorter/nicer
|
||||
__module__ = 'builtins'
|
||||
|
||||
def __init__(self, msg=None, pytrace=True, allow_module_level=False):
|
||||
OutcomeException.__init__(self, msg=msg, pytrace=pytrace)
|
||||
self.allow_module_level = allow_module_level
|
||||
|
||||
|
||||
class Failed(OutcomeException):
|
||||
""" raised from an explicit call to pytest.fail() """
|
||||
__module__ = 'builtins'
|
||||
|
||||
|
||||
class Exit(KeyboardInterrupt):
|
||||
""" raised for immediate program exits (no tracebacks/summaries)"""
|
||||
def __init__(self, msg="unknown reason"):
|
||||
@@ -546,7 +552,7 @@ def importorskip(modname, minversion=None):
|
||||
# Do not raise chained exception here(#1485)
|
||||
should_skip = True
|
||||
if should_skip:
|
||||
skip("could not import %r" %(modname,))
|
||||
raise Skipped("could not import %r" %(modname,), allow_module_level=True)
|
||||
mod = sys.modules[modname]
|
||||
if minversion is None:
|
||||
return mod
|
||||
@@ -555,10 +561,11 @@ def importorskip(modname, minversion=None):
|
||||
try:
|
||||
from pkg_resources import parse_version as pv
|
||||
except ImportError:
|
||||
skip("we have a required version for %r but can not import "
|
||||
"no pkg_resources to parse version strings." %(modname,))
|
||||
raise Skipped("we have a required version for %r but can not import "
|
||||
"pkg_resources to parse version strings." % (modname,),
|
||||
allow_module_level=True)
|
||||
if verattr is None or pv(verattr) < pv(minversion):
|
||||
skip("module %r has __version__ %r, required is: %r" %(
|
||||
modname, verattr, minversion))
|
||||
raise Skipped("module %r has __version__ %r, required is: %r" %(
|
||||
modname, verattr, minversion), allow_module_level=True)
|
||||
return mod
|
||||
|
||||
|
||||
@@ -150,7 +150,12 @@ class TestCaseFunction(pytest.Function):
|
||||
pass
|
||||
|
||||
def runtest(self):
|
||||
self._testcase(result=self)
|
||||
if self.config.pluginmanager.get_plugin("pdbinvoke") is None:
|
||||
self._testcase(result=self)
|
||||
else:
|
||||
# disables tearDown and cleanups for post mortem debugging (see #1890)
|
||||
self._testcase.debug()
|
||||
|
||||
|
||||
def _prunetraceback(self, excinfo):
|
||||
pytest.Function._prunetraceback(self, excinfo)
|
||||
|
||||
@@ -10,4 +10,4 @@ $ pip install -U pluggy==<version> --no-compile --target=_pytest/vendored_packag
|
||||
```
|
||||
|
||||
And commit the modified files. The `pluggy-<version>.dist-info` directory
|
||||
created by `pip` should be ignored.
|
||||
created by `pip` should be added as well.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
pluggy.py,sha256=v_RfWzyW6DPU1cJu_EFoL_OHq3t13qloVdR6UaMCXQA,29862
|
||||
pluggy-0.3.1.dist-info/top_level.txt,sha256=xKSCRhai-v9MckvMuWqNz16c1tbsmOggoMSwTgcpYHE,7
|
||||
pluggy-0.3.1.dist-info/pbr.json,sha256=xX3s6__wOcAyF-AZJX1sdZyW6PUXT-FkfBlM69EEUCg,47
|
||||
pluggy-0.3.1.dist-info/RECORD,,
|
||||
pluggy-0.3.1.dist-info/metadata.json,sha256=nLKltOT78dMV-00uXD6Aeemp4xNsz2q59j6ORSDeLjw,1027
|
||||
pluggy-0.3.1.dist-info/METADATA,sha256=1b85Ho2u4iK30M099k7axMzcDDhLcIMb-A82JUJZnSo,1334
|
||||
pluggy-0.3.1.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110
|
||||
pluggy-0.3.1.dist-info/DESCRIPTION.rst,sha256=P5Akh1EdIBR6CeqtV2P8ZwpGSpZiTKPw0NyS7jEiD-g,306
|
||||
@@ -1 +0,0 @@
|
||||
{"license": "MIT license", "name": "pluggy", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "plugin and hook calling mechanisms for python", "platform": "unix", "version": "0.3.1", "extensions": {"python.details": {"document_names": {"description": "DESCRIPTION.rst"}, "contacts": [{"role": "author", "email": "holger at merlinux.eu", "name": "Holger Krekel"}]}}, "classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Libraries", "Topic :: Utilities", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5"]}
|
||||
@@ -1 +0,0 @@
|
||||
{"is_release": false, "git_version": "7d4c9cd"}
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
Plugin registration and hook calling for Python
|
||||
===============================================
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
22
_pytest/vendored_packages/pluggy-0.4.0.dist-info/LICENSE.txt
Normal file
22
_pytest/vendored_packages/pluggy-0.4.0.dist-info/LICENSE.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 holger krekel (rather uses bitbucket/hpk42)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
Metadata-Version: 2.0
|
||||
Name: pluggy
|
||||
Version: 0.3.1
|
||||
Version: 0.4.0
|
||||
Summary: plugin and hook calling mechanisms for python
|
||||
Home-page: UNKNOWN
|
||||
Home-page: https://github.com/pytest-dev/pluggy
|
||||
Author: Holger Krekel
|
||||
Author-email: holger at merlinux.eu
|
||||
License: MIT license
|
||||
@@ -27,6 +27,7 @@ Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
|
||||
|
||||
Plugin registration and hook calling for Python
|
||||
===============================================
|
||||
|
||||
9
_pytest/vendored_packages/pluggy-0.4.0.dist-info/RECORD
Normal file
9
_pytest/vendored_packages/pluggy-0.4.0.dist-info/RECORD
Normal file
@@ -0,0 +1,9 @@
|
||||
pluggy.py,sha256=u0oG9cv-oLOkNvEBlwnnu8pp1AyxpoERgUO00S3rvpQ,31543
|
||||
pluggy-0.4.0.dist-info/DESCRIPTION.rst,sha256=ltvjkFd40LW_xShthp6RRVM6OB_uACYDFR3kTpKw7o4,307
|
||||
pluggy-0.4.0.dist-info/LICENSE.txt,sha256=ruwhUOyV1HgE9F35JVL9BCZ9vMSALx369I4xq9rhpkM,1134
|
||||
pluggy-0.4.0.dist-info/METADATA,sha256=pe2hbsqKFaLHC6wAQPpFPn0KlpcPfLBe_BnS4O70bfk,1364
|
||||
pluggy-0.4.0.dist-info/RECORD,,
|
||||
pluggy-0.4.0.dist-info/WHEEL,sha256=9Z5Xm-eel1bTS7e6ogYiKz0zmPEqDwIypurdHN1hR40,116
|
||||
pluggy-0.4.0.dist-info/metadata.json,sha256=T3go5L2qOa_-H-HpCZi3EoVKb8sZ3R-fOssbkWo2nvM,1119
|
||||
pluggy-0.4.0.dist-info/top_level.txt,sha256=xKSCRhai-v9MckvMuWqNz16c1tbsmOggoMSwTgcpYHE,7
|
||||
pluggy-0.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
@@ -1,5 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.24.0)
|
||||
Generator: bdist_wheel (0.29.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
@@ -0,0 +1 @@
|
||||
{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Topic :: Software Development :: Testing", "Topic :: Software Development :: Libraries", "Topic :: Utilities", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5"], "extensions": {"python.details": {"contacts": [{"email": "holger at merlinux.eu", "name": "Holger Krekel", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "https://github.com/pytest-dev/pluggy"}}}, "generator": "bdist_wheel (0.29.0)", "license": "MIT license", "metadata_version": "2.0", "name": "pluggy", "platform": "unix", "summary": "plugin and hook calling mechanisms for python", "version": "0.4.0"}
|
||||
@@ -67,8 +67,9 @@ Pluggy currently consists of functionality for:
|
||||
import sys
|
||||
import inspect
|
||||
|
||||
__version__ = '0.3.1'
|
||||
__all__ = ["PluginManager", "PluginValidationError",
|
||||
__version__ = '0.4.0'
|
||||
|
||||
__all__ = ["PluginManager", "PluginValidationError", "HookCallError",
|
||||
"HookspecMarker", "HookimplMarker"]
|
||||
|
||||
_py3 = sys.version_info > (3, 0)
|
||||
@@ -308,7 +309,7 @@ class PluginManager(object):
|
||||
""" Core Pluginmanager class which manages registration
|
||||
of plugin objects and 1:N hook calling.
|
||||
|
||||
You can register new hooks by calling ``addhooks(module_or_class)``.
|
||||
You can register new hooks by calling ``add_hookspec(module_or_class)``.
|
||||
You can register plugin objects (which contain hooks) by calling
|
||||
``register(plugin)``. The Pluginmanager is initialized with a
|
||||
prefix that is searched for in the names of the dict of registered
|
||||
@@ -374,7 +375,10 @@ class PluginManager(object):
|
||||
|
||||
def parse_hookimpl_opts(self, plugin, name):
|
||||
method = getattr(plugin, name)
|
||||
res = getattr(method, self.project_name + "_impl", None)
|
||||
try:
|
||||
res = getattr(method, self.project_name + "_impl", None)
|
||||
except Exception:
|
||||
res = {}
|
||||
if res is not None and not isinstance(res, dict):
|
||||
# false positive
|
||||
res = None
|
||||
@@ -455,6 +459,10 @@ class PluginManager(object):
|
||||
""" Return a plugin or None for the given name. """
|
||||
return self._name2plugin.get(name)
|
||||
|
||||
def has_plugin(self, name):
|
||||
""" Return True if a plugin with the given name is registered. """
|
||||
return self.get_plugin(name) is not None
|
||||
|
||||
def get_name(self, plugin):
|
||||
""" Return name for registered plugin or None if not registered. """
|
||||
for name, val in self._name2plugin.items():
|
||||
@@ -492,7 +500,8 @@ class PluginManager(object):
|
||||
def load_setuptools_entrypoints(self, entrypoint_name):
|
||||
""" Load modules from querying the specified setuptools entrypoint name.
|
||||
Return the number of loaded plugins. """
|
||||
from pkg_resources import iter_entry_points, DistributionNotFound
|
||||
from pkg_resources import (iter_entry_points, DistributionNotFound,
|
||||
VersionConflict)
|
||||
for ep in iter_entry_points(entrypoint_name):
|
||||
# is the plugin registered or blocked?
|
||||
if self.get_plugin(ep.name) or self.is_blocked(ep.name):
|
||||
@@ -501,6 +510,9 @@ class PluginManager(object):
|
||||
plugin = ep.load()
|
||||
except DistributionNotFound:
|
||||
continue
|
||||
except VersionConflict as e:
|
||||
raise PluginValidationError(
|
||||
"Plugin %r could not be loaded: %s!" % (ep.name, e))
|
||||
self.register(plugin, name=ep.name)
|
||||
self._plugin_distinfo.append((plugin, ep.dist))
|
||||
return len(self._plugin_distinfo)
|
||||
@@ -573,7 +585,7 @@ class _MultiCall:
|
||||
|
||||
# XXX note that the __multicall__ argument is supported only
|
||||
# for pytest compatibility reasons. It was never officially
|
||||
# supported there and is explicitly deprecated since 2.8
|
||||
# supported there and is explicitely deprecated since 2.8
|
||||
# so we can remove it soon, allowing to avoid the below recursion
|
||||
# in execute() and simplify/speed up the execute loop.
|
||||
|
||||
@@ -590,7 +602,13 @@ class _MultiCall:
|
||||
|
||||
while self.hook_impls:
|
||||
hook_impl = self.hook_impls.pop()
|
||||
args = [all_kwargs[argname] for argname in hook_impl.argnames]
|
||||
try:
|
||||
args = [all_kwargs[argname] for argname in hook_impl.argnames]
|
||||
except KeyError:
|
||||
for argname in hook_impl.argnames:
|
||||
if argname not in all_kwargs:
|
||||
raise HookCallError(
|
||||
"hook call must provide argument %r" % (argname,))
|
||||
if hook_impl.hookwrapper:
|
||||
return _wrapped_call(hook_impl.function(*args), self.execute)
|
||||
res = hook_impl.function(*args)
|
||||
@@ -629,7 +647,10 @@ def varnames(func, startindex=None):
|
||||
startindex = 1
|
||||
else:
|
||||
if not inspect.isfunction(func) and not inspect.ismethod(func):
|
||||
func = getattr(func, '__call__', func)
|
||||
try:
|
||||
func = getattr(func, '__call__', func)
|
||||
except Exception:
|
||||
return ()
|
||||
if startindex is None:
|
||||
startindex = int(inspect.ismethod(func))
|
||||
|
||||
@@ -763,6 +784,10 @@ class PluginValidationError(Exception):
|
||||
""" plugin failed validation. """
|
||||
|
||||
|
||||
class HookCallError(Exception):
|
||||
""" Hook was called wrongly. """
|
||||
|
||||
|
||||
if hasattr(inspect, 'signature'):
|
||||
def _formatdef(func):
|
||||
return "%s%s" % (
|
||||
|
||||
@@ -8,9 +8,10 @@ environment:
|
||||
matrix:
|
||||
# create multiple jobs to execute a set of tox runs on each; this is to workaround having
|
||||
# builds timing out in AppVeyor
|
||||
- TOXENV: "linting,py26,py27,py33,py34,py35,pypy"
|
||||
# pypy is disabled until #1963 gets fixed
|
||||
- TOXENV: "linting,py26,py27,py33,py34,py35"
|
||||
- TOXENV: "py27-pexpect,py27-xdist,py27-trial,py35-pexpect,py35-xdist,py35-trial"
|
||||
- TOXENV: "py27-nobyte,doctesting,freeze"
|
||||
- TOXENV: "py27-nobyte,doctesting,freeze,docs"
|
||||
|
||||
install:
|
||||
- echo Installed Pythons
|
||||
|
||||
@@ -19,10 +19,9 @@ REGENDOC_ARGS := \
|
||||
--normalize "@/tmp/pytest-of-.*/pytest-\d+@PYTEST_TMPDIR@" \
|
||||
|
||||
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||
|
||||
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@@ -36,22 +35,6 @@ help:
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
SITETARGET=$(shell ./_getdoctarget.py)
|
||||
|
||||
showtarget:
|
||||
@echo $(SITETARGET)
|
||||
|
||||
install: html
|
||||
# for access talk to someone with login rights to
|
||||
# pytest-dev@pytest.org to add your ssh key
|
||||
rsync -avz _build/html/ pytest-dev@pytest.org:pytest.org/$(SITETARGET)
|
||||
|
||||
installpdf: latexpdf
|
||||
@scp $(BUILDDIR)/latex/pytest.pdf pytest-dev@pytest.org:pytest.org/$(SITETARGET)
|
||||
|
||||
installall: clean install installpdf
|
||||
@echo "done"
|
||||
|
||||
regen:
|
||||
PYTHONDONTWRITEBYTECODE=1 COLUMNS=76 regendoc --update *.rst */*.rst ${REGENDOC_ARGS}
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import py
|
||||
|
||||
def get_version_string():
|
||||
fn = py.path.local(__file__).join("..", "..", "..",
|
||||
"_pytest", "__init__.py")
|
||||
for line in fn.readlines():
|
||||
if "version" in line and not line.strip().startswith('#'):
|
||||
return eval(line.split("=")[-1])
|
||||
|
||||
def get_minor_version_string():
|
||||
return ".".join(get_version_string().split(".")[:2])
|
||||
|
||||
if __name__ == "__main__":
|
||||
print (get_minor_version_string())
|
||||
@@ -6,6 +6,6 @@
|
||||
<li><a href="https://github.com/pytest-dev/pytest/">pytest @ GitHub</a></li>
|
||||
<li><a href="http://plugincompat.herokuapp.com/">3rd party plugins</a></li>
|
||||
<li><a href="https://github.com/pytest-dev/pytest/issues">Issue Tracker</a></li>
|
||||
<li><a href="http://pytest.org/latest/pytest.pdf">PDF Documentation</a>
|
||||
<li><a href="https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf">PDF Documentation</a>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
:orphan:
|
||||
|
||||
.. warnings about this file not being included in any toctree will be suppressed by :orphan:
|
||||
|
||||
|
||||
April 2015 is "adopt pytest month"
|
||||
=============================================
|
||||
|
||||
@@ -5,7 +5,11 @@ Release announcements
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
|
||||
release-3.0.3
|
||||
release-3.0.2
|
||||
release-3.0.1
|
||||
release-3.0.0
|
||||
sprint2016
|
||||
release-2.9.2
|
||||
release-2.9.1
|
||||
|
||||
@@ -41,7 +41,7 @@ Changes 2.6.3
|
||||
dep). Thanks Charles Cloud for analysing the issue.
|
||||
|
||||
- fix conftest related fixture visibility issue: when running with a
|
||||
CWD outside 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.
|
||||
|
||||
- Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the
|
||||
|
||||
@@ -63,3 +63,5 @@ The py.test Development Team
|
||||
.. _#649: https://github.com/pytest-dev/pytest/issues/649
|
||||
|
||||
.. _@asottile: https://github.com/asottile
|
||||
.. _@nicoddemus: https://github.com/nicoddemus
|
||||
.. _@tomviner: https://github.com/tomviner
|
||||
|
||||
@@ -69,5 +69,10 @@ The py.test Development Team
|
||||
.. _#1496: https://github.com/pytest-dev/pytest/issue/1496
|
||||
.. _#1524: https://github.com/pytest-dev/pytest/issue/1524
|
||||
|
||||
.. _@prusse-martin: https://github.com/prusse-martin
|
||||
.. _@astraw38: https://github.com/astraw38
|
||||
.. _@hackebrot: https://github.com/hackebrot
|
||||
.. _@omarkohl: https://github.com/omarkohl
|
||||
.. _@pquentin: https://github.com/pquentin
|
||||
.. _@prusse-martin: https://github.com/prusse-martin
|
||||
.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
|
||||
.. _@tomviner: https://github.com/tomviner
|
||||
|
||||
@@ -6,7 +6,7 @@ The pytest team is proud to announce the 3.0.0 release!
|
||||
pytest is a mature Python testing tool with more than a 1600 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
|
||||
This release contains a lot of bugs and improvements, and much of
|
||||
This release contains a lot of bugs fixes and improvements, and much of
|
||||
the work done on it was possible because of the 2016 Sprint[1], which
|
||||
was funded by an indiegogo campaign which raised over US$12,000 with
|
||||
nearly 100 backers.
|
||||
@@ -76,7 +76,7 @@ Thanks to all who contributed to this release, among them:
|
||||
|
||||
|
||||
Happy testing,
|
||||
The py.test Development Team
|
||||
The Pytest Development Team
|
||||
|
||||
[1] http://blog.pytest.org/2016/pytest-development-sprint/
|
||||
[2] http://blog.pytest.org/2016/whats-new-in-pytest-30/
|
||||
|
||||
26
doc/en/announce/release-3.0.1.rst
Normal file
26
doc/en/announce/release-3.0.1.rst
Normal file
@@ -0,0 +1,26 @@
|
||||
pytest-3.0.1
|
||||
============
|
||||
|
||||
pytest 3.0.1 has just been released to PyPI.
|
||||
|
||||
This release fixes some regressions reported in version 3.0.0, 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:
|
||||
|
||||
Adam Chainz
|
||||
Andrew Svetlov
|
||||
Bruno Oliveira
|
||||
Daniel Hahler
|
||||
Dmitry Dygalo
|
||||
Florian Bruhin
|
||||
Marcin Bachry
|
||||
Ronny Pfannschmidt
|
||||
matthiasha
|
||||
|
||||
Happy testing,
|
||||
The py.test Development Team
|
||||
24
doc/en/announce/release-3.0.2.rst
Normal file
24
doc/en/announce/release-3.0.2.rst
Normal file
@@ -0,0 +1,24 @@
|
||||
pytest-3.0.2
|
||||
============
|
||||
|
||||
pytest 3.0.2 has just been released to PyPI.
|
||||
|
||||
This release fixes some regressions and bugs reported in version 3.0.1, 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:
|
||||
|
||||
* Ahn Ki-Wook
|
||||
* Bruno Oliveira
|
||||
* Florian Bruhin
|
||||
* Jordan Guymon
|
||||
* Raphael Pierzina
|
||||
* Ronny Pfannschmidt
|
||||
* mbyt
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
27
doc/en/announce/release-3.0.3.rst
Normal file
27
doc/en/announce/release-3.0.3.rst
Normal file
@@ -0,0 +1,27 @@
|
||||
pytest-3.0.3
|
||||
============
|
||||
|
||||
pytest 3.0.3 has just been released to PyPI.
|
||||
|
||||
This release fixes some regressions and bugs reported in the last version,
|
||||
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:
|
||||
|
||||
* Bruno Oliveira
|
||||
* Florian Bruhin
|
||||
* Floris Bruynooghe
|
||||
* Huayi Zhang
|
||||
* Lev Maximov
|
||||
* Raquel Alegre
|
||||
* Ronny Pfannschmidt
|
||||
* Roy Williams
|
||||
* Tyler Goodlet
|
||||
* mbyt
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -39,8 +39,6 @@ Over 20 participants took part from 4 continents, including employees
|
||||
from Splunk, Personalkollen, Cobe.io, FanDuel and Dolby. Some newcomers
|
||||
mixed with developers who have worked on pytest since its beginning, and
|
||||
of course everyone in between.
|
||||
Ana Ribeiro, Brazil
|
||||
Ronny Pfannschmidt, Germany
|
||||
|
||||
|
||||
Sprint organisation, schedule
|
||||
|
||||
@@ -26,7 +26,7 @@ you will see the return value of the function call::
|
||||
|
||||
$ pytest test_assert1.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -170,7 +170,7 @@ if you run this module::
|
||||
|
||||
$ pytest test_assert2.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -208,6 +208,7 @@ It is possible to add your own detailed explanations by implementing
|
||||
the ``pytest_assertrepr_compare`` hook.
|
||||
|
||||
.. autofunction:: _pytest.hookspec.pytest_assertrepr_compare
|
||||
:noindex:
|
||||
|
||||
As an example consider adding the following hook in a conftest.py which
|
||||
provides an alternative explanation for ``Foo`` objects::
|
||||
|
||||
@@ -58,13 +58,13 @@ Fixtures and requests
|
||||
|
||||
To mark a fixture function:
|
||||
|
||||
.. autofunction:: _pytest.python.fixture
|
||||
.. autofunction:: _pytest.fixtures.fixture
|
||||
|
||||
Tutorial at :ref:`fixtures`.
|
||||
|
||||
The ``request`` object that can be used from fixture functions.
|
||||
|
||||
.. autoclass:: _pytest.python.FixtureRequest()
|
||||
.. autoclass:: _pytest.fixtures.FixtureRequest()
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ If you then run it with ``--lf``::
|
||||
|
||||
$ pytest --lf
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
run-last-failure: rerun last 2 failures
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 50 items
|
||||
@@ -122,7 +122,7 @@ of ``FF`` and dots)::
|
||||
|
||||
$ pytest --ff
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
run-last-failure: rerun last 2 failures first
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 50 items
|
||||
@@ -227,14 +227,14 @@ You can always peek at the content of the cache using the
|
||||
|
||||
$ py.test --cache-show
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
cachedir: $REGENDOC_TMPDIR/.cache
|
||||
------------------------------- cache values -------------------------------
|
||||
cache/lastfailed contains:
|
||||
{'test_caching.py::test_function': True}
|
||||
example/value contains:
|
||||
42
|
||||
cache/lastfailed contains:
|
||||
{'test_caching.py::test_function': True}
|
||||
|
||||
======= no tests ran in 0.12 seconds ========
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ of the failing function and hide the other one::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
|
||||
@@ -19,11 +19,8 @@
|
||||
# The short X.Y version.
|
||||
|
||||
import os, sys
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
import _getdoctarget
|
||||
|
||||
version = _getdoctarget.get_minor_version_string()
|
||||
release = _getdoctarget.get_version_string()
|
||||
from _pytest import __version__ as version
|
||||
release = ".".join(version.split(".")[:2])
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
@@ -127,7 +124,7 @@ html_theme_options = {
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
html_title = None
|
||||
html_title = 'pytest documentation'
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
html_short_title = "pytest-%s" % release
|
||||
@@ -144,7 +141,7 @@ html_favicon = "img/pytest1favi.ico"
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
# html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
|
||||
@@ -10,32 +10,51 @@ Full pytest documentation
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
overview
|
||||
apiref
|
||||
example/index
|
||||
getting-started
|
||||
usage
|
||||
assert
|
||||
builtin
|
||||
fixture
|
||||
monkeypatch
|
||||
tmpdir
|
||||
capture
|
||||
recwarn
|
||||
doctest
|
||||
mark
|
||||
skipping
|
||||
parametrize
|
||||
cache
|
||||
plugins
|
||||
unittest
|
||||
nose
|
||||
xunit_setup
|
||||
plugins
|
||||
writing_plugins
|
||||
|
||||
example/index
|
||||
goodpractices
|
||||
customize
|
||||
bash-completion
|
||||
|
||||
backwards-compatibility
|
||||
license
|
||||
contributing
|
||||
talks
|
||||
projects
|
||||
faq
|
||||
contact
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
funcarg_compare
|
||||
announce/index
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:maxdepth: 1
|
||||
|
||||
changelog
|
||||
|
||||
|
||||
@@ -48,6 +48,16 @@ Here is the algorithm which finds the rootdir from ``args``:
|
||||
directory. This allows to work with pytest in structures that are not part of
|
||||
a package and don't have any particular ini-file configuration.
|
||||
|
||||
If no ``args`` are given, pytest collects test below the current working
|
||||
directory and also starts determining the rootdir from there.
|
||||
|
||||
:warning: custom pytest plugin commandline arguments may include a path, as in
|
||||
``pytest --log-output ../../test.log args``. Then ``args`` is mandatory,
|
||||
otherwise pytest uses the folder of test.log for rootdir determination
|
||||
(see also `issue 1435 <https://github.com/pytest-dev/pytest/issues/1435>`_).
|
||||
A dot ``.`` for referencing to the current working directory is also
|
||||
possible.
|
||||
|
||||
Note that an existing ``pytest.ini`` file will always be considered a match,
|
||||
whereas ``tox.ini`` and ``setup.cfg`` will only match if they contain a
|
||||
``[pytest]`` or ``[tool:pytest]`` section, respectively. Options from multiple ini-files candidates are never
|
||||
|
||||
@@ -16,6 +16,7 @@ from docstrings in all python modules (including regular
|
||||
python test modules)::
|
||||
|
||||
pytest --doctest-modules
|
||||
|
||||
You can make these changes permanent in your project by
|
||||
putting them into a pytest.ini file like this:
|
||||
|
||||
@@ -48,7 +49,7 @@ then you can just invoke ``pytest`` without command line options::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 1 items
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[pytest]
|
||||
testfilepatterns =
|
||||
${topdir}/tests/unit/test_${basename}
|
||||
${topdir}/tests/functional/*.py
|
||||
@@ -31,7 +31,7 @@ You can then restrict a test run to only run tests marked with ``webtest``::
|
||||
|
||||
$ pytest -v -m webtest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -45,7 +45,7 @@ Or the inverse, running all tests except the webtest ones::
|
||||
|
||||
$ pytest -v -m "not webtest"
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
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
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 5 items
|
||||
@@ -79,7 +79,7 @@ You can also select on the class::
|
||||
|
||||
$ pytest -v test_server.py::TestClass
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -92,7 +92,7 @@ Or select multiple nodes::
|
||||
|
||||
$ pytest -v test_server.py::TestClass test_server.py::test_send_http
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
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
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
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
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -160,7 +160,7 @@ Or to select "http" and "quick" tests::
|
||||
|
||||
$ pytest -k "http or quick" -v
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -352,7 +352,7 @@ the test needs::
|
||||
|
||||
$ pytest -E stage2
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -364,7 +364,7 @@ and here is one that specifies exactly the environment needed::
|
||||
|
||||
$ pytest -E stage1
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -485,7 +485,7 @@ then you will see two test skipped and two executed tests as expected::
|
||||
|
||||
$ pytest -rs # this option reports skip reasons
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -499,7 +499,7 @@ Note that if you specify a platform via the marker-command line option like this
|
||||
|
||||
$ pytest -m linux2
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -551,7 +551,7 @@ We can now use the ``-m option`` to select one set::
|
||||
|
||||
$ pytest -m interface --tb=short
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -573,7 +573,7 @@ or to select both "event" and "interface" tests::
|
||||
|
||||
$ pytest -m "interface or event" --tb=short
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ now execute the test specification::
|
||||
|
||||
nonpython $ pytest test_simple.yml
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -59,7 +59,7 @@ consulted when reporting in ``verbose`` mode::
|
||||
|
||||
nonpython $ pytest -v
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
collecting ... collected 2 items
|
||||
@@ -81,7 +81,7 @@ interesting to just look at the collection tree::
|
||||
|
||||
nonpython $ pytest --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
collected 2 items
|
||||
<YamlFile 'test_simple.yml'>
|
||||
|
||||
@@ -130,7 +130,7 @@ objects, they are still using the default pytest representation::
|
||||
|
||||
$ pytest test_time.py --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 6 items
|
||||
<Module 'test_time.py'>
|
||||
@@ -181,7 +181,7 @@ this is a fully self-contained example which you can run with::
|
||||
|
||||
$ pytest test_scenarios.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
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
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
<Module 'test_scenarios.py'>
|
||||
@@ -259,7 +259,7 @@ Let's first see how it looks like at collection time::
|
||||
|
||||
$ pytest test_backends.py --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
<Module 'test_backends.py'>
|
||||
@@ -320,7 +320,7 @@ The result of this test will be successful::
|
||||
|
||||
$ pytest test_indirect_list.py --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
<Module 'test_indirect_list.py'>
|
||||
@@ -447,7 +447,7 @@ If you run this with reporting for skips enabled::
|
||||
|
||||
$ pytest -rs test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ then the test collection looks like this::
|
||||
|
||||
$ pytest --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 2 items
|
||||
<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
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 3 items
|
||||
<Module 'CWD/pythoncollection.py'>
|
||||
@@ -230,7 +230,7 @@ will be left out::
|
||||
|
||||
$ pytest --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 0 items
|
||||
|
||||
|
||||
@@ -7,13 +7,11 @@ Demo of Python failure reports with pytest
|
||||
Here is a nice run of several tens of failures
|
||||
and how ``pytest`` presents things (unfortunately
|
||||
not showing the nice colors here in the HTML that you
|
||||
get on the terminal - we are working on that):
|
||||
|
||||
.. code-block:: python
|
||||
get on the terminal - we are working on that)::
|
||||
|
||||
assertion $ pytest failure_demo.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR/assertion, inifile:
|
||||
collected 42 items
|
||||
|
||||
@@ -361,7 +359,7 @@ get on the terminal - we are working on that):
|
||||
> int(s)
|
||||
E ValueError: invalid literal for int() with base 10: 'qwe'
|
||||
|
||||
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1174>:1: ValueError
|
||||
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1190>:1: ValueError
|
||||
_______ TestRaises.test_raises_doesnt ________
|
||||
|
||||
self = <failure_demo.TestRaises object at 0xdeadbeef>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
.. highlightlang:: python
|
||||
|
||||
Basic patterns and examples
|
||||
==========================================================
|
||||
@@ -10,7 +9,9 @@ Pass different values to a test function, depending on command line options
|
||||
.. regendoc:wipe
|
||||
|
||||
Suppose we want to write a test that depends on a command line option.
|
||||
Here is a basic pattern to achieve this::
|
||||
Here is a basic pattern to achieve this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_sample.py
|
||||
def test_answer(cmdopt):
|
||||
@@ -22,7 +23,9 @@ Here is a basic pattern to achieve this::
|
||||
|
||||
|
||||
For this to work we need to add a command line option and
|
||||
provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`::
|
||||
provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import pytest
|
||||
@@ -91,7 +94,9 @@ Dynamically adding command line options
|
||||
|
||||
Through :confval:`addopts` you can statically add command line
|
||||
options for your project. You can also dynamically modify
|
||||
the command line arguments before they get processed::
|
||||
the command line arguments before they get processed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
import sys
|
||||
@@ -101,14 +106,14 @@ the command line arguments before they get processed::
|
||||
num = max(multiprocessing.cpu_count() / 2, 1)
|
||||
args[:] = ["-n", str(num)] + args
|
||||
|
||||
If you have the :ref:`xdist plugin <xdist>` installed
|
||||
If you have the `xdist plugin <https://pypi.python.org/pypi/pytest-xdist>`_ installed
|
||||
you will now always perform test runs using a number
|
||||
of subprocesses close to your CPU. Running in an empty
|
||||
directory with the above conftest.py::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 0 items
|
||||
|
||||
@@ -122,7 +127,9 @@ Control skipping of tests according to command line option
|
||||
.. regendoc:wipe
|
||||
|
||||
Here is a ``conftest.py`` file adding a ``--runslow`` command
|
||||
line option to control skipping of ``slow`` marked tests::
|
||||
line option to control skipping of ``slow`` marked tests:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
@@ -131,10 +138,11 @@ line option to control skipping of ``slow`` marked tests::
|
||||
parser.addoption("--runslow", action="store_true",
|
||||
help="run slow tests")
|
||||
|
||||
We can now write a test module like this::
|
||||
We can now write a test module like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@@ -156,13 +164,13 @@ and when running it will see a skipped "slow" test::
|
||||
|
||||
$ pytest -rs # "-rs" means report details on the little 's'
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
test_module.py .s
|
||||
======= short test summary info ========
|
||||
SKIP [1] test_module.py:14: need --runslow option to run
|
||||
SKIP [1] test_module.py:13: need --runslow option to run
|
||||
|
||||
======= 1 passed, 1 skipped in 0.12 seconds ========
|
||||
|
||||
@@ -170,7 +178,7 @@ Or run it including the ``slow`` marked test::
|
||||
|
||||
$ pytest --runslow
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -187,7 +195,9 @@ If you have a test helper function called from a test you can
|
||||
use the ``pytest.fail`` marker to fail a test with a certain message.
|
||||
The test support function will not show up in the traceback if you
|
||||
set the ``__tracebackhide__`` option somewhere in the helper function.
|
||||
Example::
|
||||
Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_checkconfig.py
|
||||
import pytest
|
||||
@@ -218,7 +228,9 @@ Let's run our little function::
|
||||
|
||||
If you only want to hide certain exceptions, you can set ``__tracebackhide__``
|
||||
to a callable which gets the ``ExceptionInfo`` object. You can for example use
|
||||
this to make sure unexpected exception types aren't hidden::
|
||||
this to make sure unexpected exception types aren't hidden:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import operator
|
||||
import pytest
|
||||
@@ -246,7 +258,9 @@ Detect if running from within a pytest run
|
||||
Usually it is a bad idea to make application code
|
||||
behave differently if called from a test. But if you
|
||||
absolutely must find out if your application code is
|
||||
running from a test you can do something like this::
|
||||
running from a test you can do something like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
@@ -257,7 +271,9 @@ running from a test you can do something like this::
|
||||
def pytest_unconfigure(config):
|
||||
del sys._called_from_test
|
||||
|
||||
and then check for the ``sys._called_from_test`` flag::
|
||||
and then check for the ``sys._called_from_test`` flag:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
if hasattr(sys, '_called_from_test'):
|
||||
# called from within a test run
|
||||
@@ -273,7 +289,9 @@ Adding info to test report header
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
It's easy to present extra information in a ``pytest`` run::
|
||||
It's easy to present extra information in a ``pytest`` run:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
@@ -284,7 +302,7 @@ which will add the string to the test header accordingly::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
project deps: mylib-1.1
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 0 items
|
||||
@@ -293,10 +311,11 @@ which will add the string to the test header accordingly::
|
||||
|
||||
.. regendoc:wipe
|
||||
|
||||
You can also return a list of strings which will be considered as several
|
||||
lines of information. You can of course also make the amount of reporting
|
||||
information on e.g. the value of ``config.getoption('verbose')`` so that
|
||||
you present more information appropriately::
|
||||
It is also possible to return a list of strings which will be considered as several
|
||||
lines of information. You may consider ``config.getoption('verbose')`` in order to
|
||||
display more information if applicable:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
@@ -308,7 +327,7 @@ which will add info only when run with "--v"::
|
||||
|
||||
$ pytest -v
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
info1: did you know that ...
|
||||
did you?
|
||||
@@ -321,7 +340,7 @@ and nothing when run plainly::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 0 items
|
||||
|
||||
@@ -335,10 +354,11 @@ profiling test duration
|
||||
.. versionadded: 2.2
|
||||
|
||||
If you have a slow running large test suite you might want to find
|
||||
out which tests are the slowest. Let's make an artificial test suite::
|
||||
out which tests are the slowest. Let's make an artificial test suite:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_some_are_slow.py
|
||||
|
||||
import time
|
||||
|
||||
def test_funcfast():
|
||||
@@ -354,7 +374,7 @@ Now we can profile which test functions execute the slowest::
|
||||
|
||||
$ pytest --durations=3
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 3 items
|
||||
|
||||
@@ -375,7 +395,9 @@ Sometimes you may have a testing situation which consists of a series
|
||||
of test steps. If one step fails it makes no sense to execute further
|
||||
steps as they are all expected to fail anyway and their tracebacks
|
||||
add no insight. Here is a simple ``conftest.py`` file which introduces
|
||||
an ``incremental`` marker which is to be used on classes::
|
||||
an ``incremental`` marker which is to be used on classes:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
@@ -394,7 +416,9 @@ an ``incremental`` marker which is to be used on classes::
|
||||
pytest.xfail("previous test failed (%s)" %previousfailed.name)
|
||||
|
||||
These two hook implementations work together to abort incremental-marked
|
||||
tests in a class. Here is a test module example::
|
||||
tests in a class. Here is a test module example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_step.py
|
||||
|
||||
@@ -416,7 +440,7 @@ If we run this::
|
||||
|
||||
$ pytest -rx
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -452,7 +476,9 @@ concept. It's however recommended to have explicit fixture references in your
|
||||
tests or test classes rather than relying on implicitly executing
|
||||
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 a an example for making a ``db`` fixture available in a directory:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of a/conftest.py
|
||||
import pytest
|
||||
@@ -464,20 +490,26 @@ Here is a an example for making a ``db`` fixture available in a directory::
|
||||
def db():
|
||||
return DB()
|
||||
|
||||
and then a test module in that directory::
|
||||
and then a test module in that directory:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of a/test_db.py
|
||||
def test_a1(db):
|
||||
assert 0, db # to show value
|
||||
|
||||
another test module::
|
||||
another test module:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of a/test_db2.py
|
||||
def test_a2(db):
|
||||
assert 0, db # to show value
|
||||
|
||||
and then a module in a sister directory which will not see
|
||||
the ``db`` fixture::
|
||||
the ``db`` fixture:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of b/test_error.py
|
||||
def test_root(db): # no db here, will error out
|
||||
@@ -487,7 +519,7 @@ We can run this::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 7 items
|
||||
|
||||
@@ -553,7 +585,9 @@ 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
|
||||
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 we just write some informations out to a ``failures`` file::
|
||||
case we just write some informations out to a ``failures`` file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
@@ -579,7 +613,9 @@ case we just write some informations out to a ``failures`` file::
|
||||
f.write(rep.nodeid + extra + "\n")
|
||||
|
||||
|
||||
if you then have failing tests::
|
||||
if you then have failing tests:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
def test_fail1(tmpdir):
|
||||
@@ -591,7 +627,7 @@ and run them::
|
||||
|
||||
$ pytest test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -628,7 +664,9 @@ Making test result information available in fixtures
|
||||
.. regendoc:wipe
|
||||
|
||||
If you want to make test result reports available in fixture finalizers
|
||||
here is a little example implemented via a local plugin::
|
||||
here is a little example implemented via a local plugin:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
|
||||
@@ -658,7 +696,9 @@ here is a little example implemented via a local plugin::
|
||||
print ("executing test failed", request.node.nodeid)
|
||||
|
||||
|
||||
if you then have failing tests::
|
||||
if you then have failing tests:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_module.py
|
||||
|
||||
@@ -681,7 +721,7 @@ and run it::
|
||||
|
||||
$ pytest -s test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 3 items
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
What users say:
|
||||
|
||||
`py.test is pretty much the best thing ever`_ (Alex Gaynor)
|
||||
|
||||
|
||||
.. _`py.test is pretty much the best thing ever`_ (Alex Gaynor)
|
||||
http://twitter.com/#!/alex_gaynor/status/22389410366
|
||||
@@ -70,7 +70,7 @@ marked ``smtp`` fixture function. Running the test looks like this::
|
||||
|
||||
$ pytest test_smtpsimple.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -188,7 +188,7 @@ inspect what is going on and can now run the tests::
|
||||
|
||||
$ pytest test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -516,7 +516,7 @@ Running the above tests results in the following test IDs being used::
|
||||
|
||||
$ pytest --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 11 items
|
||||
<Module 'test_anothersmtp.py'>
|
||||
@@ -569,7 +569,7 @@ Here we declare an ``app`` fixture which receives the previously defined
|
||||
|
||||
$ pytest -v test_appsetup.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 2 items
|
||||
@@ -638,7 +638,7 @@ Let's run the tests in verbose mode and with looking at the print-output::
|
||||
|
||||
$ pytest -v -s test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 8 items
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
:orphan:
|
||||
|
||||
.. _`funcargcompare`:
|
||||
|
||||
|
||||
@@ -19,17 +19,14 @@ Installation and Getting Started
|
||||
Installation
|
||||
----------------------------------------
|
||||
|
||||
Installation options::
|
||||
Installation::
|
||||
|
||||
pip install -U pytest # or
|
||||
easy_install -U pytest
|
||||
pip install -U pytest
|
||||
|
||||
To check your installation has installed the correct version::
|
||||
|
||||
$ pytest --version
|
||||
This is pytest version 3.0.0, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
|
||||
|
||||
If you get an error checkout :ref:`installation issues`.
|
||||
This is pytest version 3.0.3, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
@@ -49,7 +46,7 @@ That's it. You can execute the test function now::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -196,7 +193,6 @@ Here are a few suggestions where to go next:
|
||||
* :ref:`cmdline` for command line invocation examples
|
||||
* :ref:`good practices <goodpractices>` for virtualenv, test layout
|
||||
* :ref:`fixtures` for providing a functional baseline to your tests
|
||||
* :ref:`apiref` for documentation and examples on using ``pytest``
|
||||
* :ref:`plugins` managing and writing plugins
|
||||
|
||||
.. include:: links.inc
|
||||
|
||||
137
doc/en/index.rst
137
doc/en/index.rst
@@ -1,59 +1,90 @@
|
||||
:orphan:
|
||||
|
||||
.. _features:
|
||||
|
||||
pytest: helps you write better programs
|
||||
=============================================
|
||||
|
||||
**a mature full-featured Python testing tool**
|
||||
|
||||
- runs on Posix/Windows, Python 2.6, 2.7 and 3.3-3.5, PyPy and (possibly still) Jython-2.5.1
|
||||
- free and open source software, distributed under the terms of the :ref:`MIT license <license>`
|
||||
- **well tested** with more than a thousand tests against itself
|
||||
- **strict backward compatibility policy** for safe pytest upgrades
|
||||
- :ref:`comprehensive online <toc>` and `PDF documentation <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
|
||||
- many :ref:`third party plugins <extplugins>` and :ref:`builtin helpers <pytest helpers>`,
|
||||
- used in :ref:`many small and large projects and organisations <projects>`
|
||||
- comes with many :ref:`tested examples <examples>`
|
||||
|
||||
**provides easy no-boilerplate testing**
|
||||
|
||||
- makes it :ref:`easy to get started <getstarted>`,
|
||||
has many :ref:`usage options <usage>`
|
||||
- :ref:`assert with the assert statement`
|
||||
- helpful :ref:`traceback and failing assertion reporting <tbreportdemo>`
|
||||
- :ref:`print debugging <printdebugging>` and :ref:`the
|
||||
capturing of standard output during test execution <captures>`
|
||||
|
||||
**scales from simple unit to complex functional testing**
|
||||
|
||||
- :ref:`modular parametrizeable fixtures <fixture>` (new in 2.3,
|
||||
continuously improved)
|
||||
- :ref:`parametrized test functions <parametrized test functions>`
|
||||
- :ref:`mark`
|
||||
- :ref:`skipping` (improved in 2.4)
|
||||
- :ref:`distribute tests to multiple CPUs <xdistcpu>` through :ref:`xdist plugin <xdist>`
|
||||
- :ref:`continuously re-run failing tests <looponfailing>`
|
||||
- :doc:`cache`
|
||||
- flexible :ref:`Python test discovery`
|
||||
|
||||
**integrates with other testing methods and tools**:
|
||||
|
||||
- multi-paradigm: pytest can run ``nose``, ``unittest`` and
|
||||
``doctest`` style test suites, including running testcases made for
|
||||
Django and trial
|
||||
- supports :ref:`good integration practices <goodpractices>`
|
||||
- supports extended :ref:`xUnit style setup <xunitsetup>`
|
||||
- supports domain-specific :ref:`non-python tests`
|
||||
- supports generating `test coverage reports
|
||||
<https://pypi.python.org/pypi/pytest-cov>`_
|
||||
- supports :pep:`8` compliant coding styles in tests
|
||||
|
||||
**extensive plugin and customization system**:
|
||||
|
||||
- all collection, reporting, running aspects are delegated to hook functions
|
||||
- customizations can be per-directory, per-project or per PyPI released plugin
|
||||
- it is easy to add command line options or customize existing behaviour
|
||||
- :ref:`easy to write your own plugins <writing-plugins>`
|
||||
=======================================
|
||||
|
||||
|
||||
.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html
|
||||
The ``pytest`` framework makes it easy to write small tests, yet
|
||||
scales to support complex functional testing for applications and libraries.
|
||||
|
||||
An example of a simple test:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_sample.py
|
||||
def func(x):
|
||||
return x + 1
|
||||
|
||||
def test_answer():
|
||||
assert func(3) == 5
|
||||
|
||||
|
||||
To execute it::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
test_sample.py F
|
||||
|
||||
======= FAILURES ========
|
||||
_______ test_answer ________
|
||||
|
||||
def test_answer():
|
||||
> assert func(3) == 5
|
||||
E assert 4 == 5
|
||||
E + where 4 = func(3)
|
||||
|
||||
test_sample.py:5: AssertionError
|
||||
======= 1 failed in 0.12 seconds ========
|
||||
|
||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used.
|
||||
See :ref:`Getting Started <getstarted>` for more examples.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Detailed info on failing :ref:`assert statements <assert>` (no need to remember ``self.assert*`` names);
|
||||
|
||||
- :ref:`Auto-discovery <test discovery>` of test modules and functions;
|
||||
|
||||
- :ref:`Modular fixtures <fixture>` for managing small or parametrized long-lived test resources;
|
||||
|
||||
- Can run :ref:`unittest <unittest>` (including trial) and :ref:`nose <noseintegration>` test suites out of the box;
|
||||
|
||||
- Python2.6+, Python3.3+, PyPy-2.3, Jython-2.5 (untested);
|
||||
|
||||
- Rich plugin architecture, with over 150+ :ref:`external plugins <extplugins>` and thriving community;
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Please see :ref:`Contents <toc>` for full documentation, including installation, tutorials and PDF documents.
|
||||
|
||||
|
||||
Bugs/Requests
|
||||
-------------
|
||||
|
||||
Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
|
||||
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
Consult the :ref:`Changelog <changelog>` page for fixes and enhancements of each version.
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright Holger Krekel and others, 2004-2016.
|
||||
|
||||
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
||||
|
||||
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE
|
||||
@@ -54,32 +54,12 @@ This autouse fixture will be executed for each test function and it
|
||||
will delete the method ``request.session.Session.request``
|
||||
so that any attempts within tests to create http requests will fail.
|
||||
|
||||
example: setting an environment variable for the test session
|
||||
-------------------------------------------------------------
|
||||
|
||||
If you would like for an environment variable to be
|
||||
configured for the entire test session, you can add this to your
|
||||
top-level ``conftest.py`` file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# content of conftest.py
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def enable_debugging(monkeypatch):
|
||||
monkeypatch.setenv("DEBUGGING_VERBOSITY", "4")
|
||||
|
||||
This auto-use fixture will set the ``DEBUGGING_VERBOSITY`` environment variable for
|
||||
the entire test session.
|
||||
|
||||
Note that the ability to use a ``monkeypatch`` fixture from a ``session``-scoped
|
||||
fixture was added in pytest-3.0.
|
||||
|
||||
|
||||
Method reference of the monkeypatch fixture
|
||||
-------------------------------------------
|
||||
|
||||
.. autoclass:: MonkeyPatch
|
||||
:members: setattr, replace, delattr, setitem, delitem, setenv, delenv, syspath_prepend, chdir, undo
|
||||
:members:
|
||||
|
||||
``monkeypatch.setattr/delattr/delitem/delenv()`` all
|
||||
by default raise an Exception if the target does not exist.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _`noseintegration`:
|
||||
|
||||
Running tests written for nose
|
||||
=======================================
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
==================================================
|
||||
Getting started basics
|
||||
==================================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
getting-started
|
||||
usage
|
||||
goodpractices
|
||||
projects
|
||||
faq
|
||||
|
||||
@@ -55,7 +55,7 @@ them in turn::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 3 items
|
||||
|
||||
@@ -103,7 +103,7 @@ Let's run this::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 3 items
|
||||
|
||||
@@ -215,6 +215,4 @@ The **metafunc** object
|
||||
|
||||
.. currentmodule:: _pytest.python
|
||||
.. autoclass:: Metafunc
|
||||
|
||||
.. automethod:: Metafunc.parametrize
|
||||
.. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists)
|
||||
:members:
|
||||
|
||||
@@ -37,7 +37,7 @@ Here is a little annotated list for some popular plugins:
|
||||
to distribute tests to CPUs and remote hosts, to run in boxed
|
||||
mode which allows to survive segmentation faults, to run in
|
||||
looponfailing mode, automatically re-running failing tests
|
||||
on file changes, see also :ref:`xdist`
|
||||
on file changes.
|
||||
|
||||
* `pytest-instafail <http://pypi.python.org/pypi/pytest-instafail>`_:
|
||||
to report failures while the test run is happening.
|
||||
@@ -144,7 +144,7 @@ in the `pytest repository <https://github.com/pytest-dev/pytest>`_.
|
||||
_pytest.monkeypatch
|
||||
_pytest.nose
|
||||
_pytest.pastebin
|
||||
_pytest.pdb
|
||||
_pytest.debugging
|
||||
_pytest.pytester
|
||||
_pytest.python
|
||||
_pytest.recwarn
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
:orphan:
|
||||
|
||||
=========================
|
||||
Parametrize with fixtures
|
||||
=========================
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.. _assertwarnings:
|
||||
|
||||
Asserting Warnings
|
||||
=====================================================
|
||||
|
||||
@@ -224,7 +224,7 @@ Running it with the report-on-xfail option gives this output::
|
||||
|
||||
example $ pytest -rx xfail_demo.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR/example, inifile:
|
||||
collected 7 items
|
||||
|
||||
@@ -293,6 +293,20 @@ imperatively, in test or setup code::
|
||||
# or
|
||||
pytest.skip("unsupported configuration")
|
||||
|
||||
Note that calling ``pytest.skip`` at the module level
|
||||
is not allowed since pytest 3.0. If you are upgrading
|
||||
and ``pytest.skip`` was being used at the module level, you can set a
|
||||
``pytestmark`` variable:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# before pytest 3.0
|
||||
pytest.skip('skipping all tests because of reasons')
|
||||
# after pytest 3.0
|
||||
pytestmark = pytest.mark.skip('skipping all tests because of reasons')
|
||||
|
||||
``pytestmark`` applies a mark or list of marks to all tests in a module.
|
||||
|
||||
|
||||
Skipping on a missing import dependency
|
||||
--------------------------------------------------
|
||||
@@ -371,3 +385,27 @@ The equivalent with "boolean conditions" is::
|
||||
imported before pytest's argument parsing takes place. For example,
|
||||
``conftest.py`` files are imported before command line parsing and thus
|
||||
``config.getvalue()`` will not execute correctly.
|
||||
|
||||
|
||||
Summary
|
||||
-------
|
||||
|
||||
Here's a quick guide on how to skip tests in a module in different situations:
|
||||
|
||||
1. Skip all tests in a module unconditionally:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytestmark = pytest.mark.skip('all tests still WIP')
|
||||
|
||||
2. Skip all tests in a module based on some condition:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytestmark = pytest.mark.skipif(sys.platform == 'win32', 'tests for linux only')
|
||||
|
||||
3. Skip all tests in a module if some import is missing:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pexpect = pytest.importorskip('pexpect')
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
pytest development status
|
||||
================================
|
||||
|
||||
https://travis-ci.org/pytest-dev/pytest
|
||||
|
||||
@@ -29,7 +29,7 @@ Running this would result in a passed test except for the last
|
||||
|
||||
$ pytest test_tmpdir.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
.. _`unittest.TestCase`:
|
||||
.. _`unittest`:
|
||||
|
||||
Support for unittest.TestCase / Integration of fixtures
|
||||
=====================================================================
|
||||
@@ -32,6 +33,17 @@ distributing tests to multiple CPUs via the ``-nNUM`` option if you
|
||||
installed the ``pytest-xdist`` plugin. Please refer to
|
||||
the general ``pytest`` documentation for many more examples.
|
||||
|
||||
.. note::
|
||||
|
||||
Running tests from ``unittest.TestCase`` subclasses with ``--pdb`` will
|
||||
disable tearDown and cleanup methods for the case that an Exception
|
||||
occurs. This allows proper post mortem debugging for all applications
|
||||
which have significant logic in their tearDown machinery. However,
|
||||
supporting this feature has the following side effect: If people
|
||||
overwrite ``unittest.TestCase`` ``__call__`` or ``run``, they need to
|
||||
to overwrite ``debug`` in the same way (this is also true for standard
|
||||
unittest).
|
||||
|
||||
Mixing pytest fixtures into unittest.TestCase style tests
|
||||
-----------------------------------------------------------
|
||||
|
||||
@@ -88,7 +100,7 @@ the ``self.db`` values in the traceback::
|
||||
|
||||
$ pytest test_unittest_db.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@ can use like this::
|
||||
pass
|
||||
""")
|
||||
result = testdir.runpytest("--verbose")
|
||||
result.fnmatch_lines("""
|
||||
result.stdout.fnmatch_lines("""
|
||||
test_example*
|
||||
""")
|
||||
|
||||
@@ -460,7 +460,7 @@ Hooks are usually declared as do-nothing functions that contain only
|
||||
documentation describing when the hook will be called and what return values
|
||||
are expected.
|
||||
|
||||
For an example, see `newhooks.py`_ from :ref:`xdist`.
|
||||
For an example, see `newhooks.py`_ from `xdist <https://github.com/pytest-dev/pytest-xdist>`_.
|
||||
|
||||
.. _`newhooks.py`: https://github.com/pytest-dev/pytest-xdist/blob/974bd566c599dc6a9ea291838c6f226197208b46/xdist/newhooks.py
|
||||
|
||||
@@ -623,7 +623,7 @@ Reference of objects involved in hooks
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: _pytest.python.FixtureDef()
|
||||
.. autoclass:: _pytest.fixtures.FixtureDef()
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -644,7 +644,7 @@ Reference of objects involved in hooks
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: pluggy.PluginManager()
|
||||
.. autoclass:: _pytest.vendored_packages.pluggy.PluginManager()
|
||||
:members:
|
||||
|
||||
.. currentmodule:: _pytest.pytester
|
||||
|
||||
197
doc/en/xdist.rst
197
doc/en/xdist.rst
@@ -1,197 +0,0 @@
|
||||
|
||||
.. _`xdist`:
|
||||
|
||||
xdist: pytest distributed testing plugin
|
||||
===============================================================
|
||||
|
||||
The `pytest-xdist`_ plugin extends ``pytest`` with some unique
|
||||
test execution modes:
|
||||
|
||||
* Looponfail: run your tests repeatedly in a subprocess. After each
|
||||
run, ``pytest`` waits until a file in your project changes and then
|
||||
re-runs the previously failing tests. This is repeated until all
|
||||
tests pass. At this point a full run is again performed.
|
||||
|
||||
* multiprocess Load-balancing: if you have multiple CPUs or hosts you can use
|
||||
them for a combined test run. This allows to speed up
|
||||
development or to use special resources of remote machines.
|
||||
|
||||
* Multi-Platform coverage: you can specify different Python interpreters
|
||||
or different platforms and run tests in parallel on all of them.
|
||||
|
||||
Before running tests remotely, ``pytest`` efficiently "rsyncs" your
|
||||
program source code to the remote place. All test results
|
||||
are reported back and displayed to your local terminal.
|
||||
You may specify different Python versions and interpreters.
|
||||
|
||||
|
||||
Installation of xdist plugin
|
||||
------------------------------
|
||||
|
||||
Install the plugin with::
|
||||
|
||||
easy_install pytest-xdist
|
||||
|
||||
# or
|
||||
|
||||
pip install pytest-xdist
|
||||
|
||||
or use the package in develop/in-place mode with
|
||||
a checkout of the `pytest-xdist repository`_ ::
|
||||
|
||||
python setup.py develop
|
||||
|
||||
|
||||
Usage examples
|
||||
---------------------
|
||||
|
||||
.. _`xdistcpu`:
|
||||
|
||||
Speed up test runs by sending tests to multiple CPUs
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
To send tests to multiple CPUs, type::
|
||||
|
||||
pytest -n NUM
|
||||
|
||||
Especially for longer running tests or tests requiring
|
||||
a lot of I/O this can lead to considerable speed ups.
|
||||
|
||||
|
||||
Running tests in a Python subprocess
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
To instantiate a Python-2.7 subprocess and send tests to it, you may type::
|
||||
|
||||
pytest -d --tx popen//python=python2.7
|
||||
|
||||
This will start a subprocess which is run with the "python2.7"
|
||||
Python interpreter, found in your system binary lookup path.
|
||||
|
||||
If you prefix the --tx option value like this::
|
||||
|
||||
pytest -d --tx 3*popen//python=python2.7
|
||||
|
||||
then three subprocesses would be created and the tests
|
||||
will be distributed to three subprocesses and run simultanously.
|
||||
|
||||
.. _looponfailing:
|
||||
|
||||
|
||||
Running tests in looponfailing mode
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
For refactoring a project with a medium or large test suite
|
||||
you can use the looponfailing mode. Simply add the ``--f`` option::
|
||||
|
||||
pytest -f
|
||||
|
||||
and ``pytest`` will run your tests. Assuming you have failures it will then
|
||||
wait for file changes and re-run the failing test set. File changes are detected by looking at ``looponfailingroots`` root directories and all of their contents (recursively). If the default for this value does not work for you you
|
||||
can change it in your project by setting a configuration option::
|
||||
|
||||
# content of a pytest.ini or tox.ini file
|
||||
[pytest]
|
||||
looponfailroots = mypkg testdir
|
||||
|
||||
This would lead to only looking for file changes in the respective directories, specified relatively to the ini-file's directory.
|
||||
|
||||
Sending tests to remote SSH accounts
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Suppose you have a package ``mypkg`` which contains some
|
||||
tests that you can successfully run locally. And you also
|
||||
have a ssh-reachable machine ``myhost``. Then
|
||||
you can ad-hoc distribute your tests by typing::
|
||||
|
||||
pytest -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg
|
||||
|
||||
This will synchronize your ``mypkg`` package directory
|
||||
with a remote ssh account and then collect and run your
|
||||
tests at the remote side.
|
||||
|
||||
You can specify multiple ``--rsyncdir`` directories
|
||||
to be sent to the remote side.
|
||||
|
||||
.. XXX CHECK
|
||||
|
||||
**NOTE:** For ``pytest`` to collect and send tests correctly
|
||||
you not only need to make sure all code and tests
|
||||
directories are rsynced, but that any test (sub) directory
|
||||
also has an ``__init__.py`` file because internally
|
||||
``pytest`` references tests as a fully qualified python
|
||||
module path. **You will otherwise get strange errors**
|
||||
during setup of the remote side.
|
||||
|
||||
Sending tests to remote Socket Servers
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Download the single-module `socketserver.py`_ Python program
|
||||
and run it like this::
|
||||
|
||||
python socketserver.py
|
||||
|
||||
It will tell you that it starts listening on the default
|
||||
port. You can now on your home machine specify this
|
||||
new socket host with something like this::
|
||||
|
||||
pytest -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
|
||||
|
||||
|
||||
.. _`atonce`:
|
||||
|
||||
Running tests on many platforms at once
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
The basic command to run tests on multiple platforms is::
|
||||
|
||||
pytest --dist=each --tx=spec1 --tx=spec2
|
||||
|
||||
If you specify a windows host, an OSX host and a Linux
|
||||
environment this command will send each tests to all
|
||||
platforms - and report back failures from all platforms
|
||||
at once. The specifications strings use the `xspec syntax`_.
|
||||
|
||||
.. _`xspec syntax`: http://codespeak.net/execnet/basics.html#xspec
|
||||
|
||||
.. _`socketserver.py`: http://bitbucket.org/hpk42/execnet/raw/2af991418160/execnet/script/socketserver.py
|
||||
|
||||
.. _`execnet`: http://codespeak.net/execnet
|
||||
|
||||
Specifying test exec environments in an ini file
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
pytest (since version 2.0) supports ini-style configuration.
|
||||
For example, you could make running with three subprocesses your default::
|
||||
|
||||
[pytest]
|
||||
addopts = -n3
|
||||
|
||||
You can also add default environments like this::
|
||||
|
||||
[pytest]
|
||||
addopts = --tx ssh=myhost//python=python2.7 --tx ssh=myhost//python=python2.6
|
||||
|
||||
and then just type::
|
||||
|
||||
pytest --dist=each
|
||||
|
||||
to run tests in each of the environments.
|
||||
|
||||
Specifying "rsync" dirs in an ini-file
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
In a ``pytest.ini`` or ``tox.ini`` file in your root project directory
|
||||
you may specify directories to include or to exclude in synchronisation::
|
||||
|
||||
[pytest]
|
||||
rsyncdirs = . mypkg helperpkg
|
||||
rsyncignore = .hg
|
||||
|
||||
These directory specifications are relative to the directory
|
||||
where the configuration file was found.
|
||||
|
||||
.. _`pytest-xdist`: http://pypi.python.org/pypi/pytest-xdist
|
||||
.. _`pytest-xdist repository`: http://bitbucket.org/pytest-dev/pytest-xdist
|
||||
.. _`pytest`: http://pytest.org
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
:orphan:
|
||||
|
||||
.. _yieldfixture:
|
||||
|
||||
"yield_fixture" functions
|
||||
|
||||
@@ -763,3 +763,21 @@ class TestDurationWithFixture:
|
||||
* call *test_1*
|
||||
""")
|
||||
|
||||
|
||||
def test_zipimport_hook(testdir, tmpdir):
|
||||
"""Test package loader is being used correctly (see #1837)."""
|
||||
zipapp = pytest.importorskip('zipapp')
|
||||
testdir.tmpdir.join('app').ensure(dir=1)
|
||||
testdir.makepyfile(**{
|
||||
'app/foo.py': """
|
||||
import pytest
|
||||
def main():
|
||||
pytest.main(['--pyarg', 'foo'])
|
||||
""",
|
||||
})
|
||||
target = tmpdir.join('foo.zip')
|
||||
zipapp.create_archive(str(testdir.tmpdir.join('app')), str(target), main='foo:main')
|
||||
result = testdir.runpython(target)
|
||||
assert result.ret == 0
|
||||
result.stderr.fnmatch_lines(['*not found*foo*'])
|
||||
assert 'INTERNALERROR>' not in result.stdout.str()
|
||||
|
||||
@@ -1063,6 +1063,22 @@ class TestFixtureUsages:
|
||||
"*1 error*"
|
||||
])
|
||||
|
||||
def test_invalid_scope(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@pytest.fixture(scope="functions")
|
||||
def badscope():
|
||||
pass
|
||||
|
||||
def test_nothing(badscope):
|
||||
pass
|
||||
""")
|
||||
result = testdir.runpytest_inprocess()
|
||||
result.stdout.fnmatch_lines(
|
||||
("*ValueError: fixture badscope from test_invalid_scope.py has an unsupported"
|
||||
" scope value 'functions'")
|
||||
)
|
||||
|
||||
def test_funcarg_parametrized_and_used_twice(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
|
||||
@@ -96,6 +96,14 @@ class TestMetafunc:
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6]))
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5,6]))
|
||||
|
||||
def test_parametrize_bad_scope(self, testdir):
|
||||
def func(x): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
try:
|
||||
metafunc.parametrize("x", [1], scope='doggy')
|
||||
except ValueError as ve:
|
||||
assert "has an unsupported scope value 'doggy'" in str(ve)
|
||||
|
||||
def test_parametrize_and_id(self):
|
||||
def func(x, y): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
@@ -105,6 +113,14 @@ class TestMetafunc:
|
||||
ids = [x.id for x in metafunc._calls]
|
||||
assert ids == ["basic-abc", "basic-def", "advanced-abc", "advanced-def"]
|
||||
|
||||
def test_parametrize_and_id_unicode(self):
|
||||
"""Allow unicode strings for "ids" parameter in Python 2 (##1905)"""
|
||||
def func(x): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.parametrize("x", [1, 2], ids=[u'basic', u'advanced'])
|
||||
ids = [x.id for x in metafunc._calls]
|
||||
assert ids == [u"basic", u"advanced"]
|
||||
|
||||
def test_parametrize_with_wrong_number_of_ids(self, testdir):
|
||||
def func(x, y): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
@@ -889,6 +905,45 @@ class TestMetafuncFunctional:
|
||||
"*test_function*advanced*FAILED",
|
||||
])
|
||||
|
||||
def test_fixture_parametrized_empty_ids(self, testdir):
|
||||
"""Fixtures parametrized with empty ids cause an internal error (#1849)."""
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope="module", ids=[], params=[])
|
||||
def temp(request):
|
||||
return request.param
|
||||
|
||||
def test_temp(temp):
|
||||
pass
|
||||
''')
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['* 1 skipped *'])
|
||||
|
||||
def test_parametrized_empty_ids(self, testdir):
|
||||
"""Tests parametrized with empty ids cause an internal error (#1849)."""
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.mark.parametrize('temp', [], ids=list())
|
||||
def test_temp(temp):
|
||||
pass
|
||||
''')
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['* 1 skipped *'])
|
||||
|
||||
def test_parametrized_ids_invalid_type(self, testdir):
|
||||
"""Tests parametrized with ids as non-strings (#1857)."""
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.mark.parametrize("x, expected", [(10, 20), (40, 80)], ids=(None, 2))
|
||||
def test_ids_numbers(x,expected):
|
||||
assert x * 2 == expected
|
||||
''')
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['*ids must be list of strings, found: 2 (type: int)*'])
|
||||
|
||||
def test_parametrize_with_identical_ids_get_unique_names(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@@ -930,43 +985,6 @@ class TestMetafuncFunctional:
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=5)
|
||||
|
||||
def test_parametrize_issue634(self, testdir):
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def foo(request):
|
||||
print('preparing foo-%d' % request.param)
|
||||
return 'foo-%d' % request.param
|
||||
|
||||
|
||||
def test_one(foo):
|
||||
pass
|
||||
|
||||
|
||||
def test_two(foo):
|
||||
pass
|
||||
|
||||
|
||||
test_two.test_with = (2, 3)
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
params = (1, 2, 3, 4)
|
||||
if not 'foo' in metafunc.fixturenames:
|
||||
return
|
||||
|
||||
test_with = getattr(metafunc.function, 'test_with', None)
|
||||
if test_with:
|
||||
params = test_with
|
||||
metafunc.parametrize('foo', params, indirect=True)
|
||||
|
||||
''')
|
||||
result = testdir.runpytest("-s")
|
||||
output = result.stdout.str()
|
||||
assert output.count('preparing foo-2') == 1
|
||||
assert output.count('preparing foo-3') == 1
|
||||
|
||||
def test_parametrize_issue323(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@@ -1047,6 +1065,125 @@ class TestMetafuncFunctional:
|
||||
assert expectederror in failures[0].longrepr.reprcrash.message
|
||||
|
||||
|
||||
class TestMetafuncFunctionalAuto:
|
||||
"""
|
||||
Tests related to automatically find out the correct scope for parametrized tests (#1832).
|
||||
"""
|
||||
|
||||
def test_parametrize_auto_scope(self, testdir):
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def fixture():
|
||||
return 1
|
||||
|
||||
@pytest.mark.parametrize('animal', ["dog", "cat"])
|
||||
def test_1(animal):
|
||||
assert animal in ('dog', 'cat')
|
||||
|
||||
@pytest.mark.parametrize('animal', ['fish'])
|
||||
def test_2(animal):
|
||||
assert animal == 'fish'
|
||||
|
||||
''')
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['* 3 passed *'])
|
||||
|
||||
def test_parametrize_auto_scope_indirect(self, testdir):
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def echo(request):
|
||||
return request.param
|
||||
|
||||
@pytest.mark.parametrize('animal, echo', [("dog", 1), ("cat", 2)], indirect=['echo'])
|
||||
def test_1(animal, echo):
|
||||
assert animal in ('dog', 'cat')
|
||||
assert echo in (1, 2, 3)
|
||||
|
||||
@pytest.mark.parametrize('animal, echo', [('fish', 3)], indirect=['echo'])
|
||||
def test_2(animal, echo):
|
||||
assert animal == 'fish'
|
||||
assert echo in (1, 2, 3)
|
||||
''')
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['* 3 passed *'])
|
||||
|
||||
def test_parametrize_auto_scope_override_fixture(self, testdir):
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def animal():
|
||||
return 'fox'
|
||||
|
||||
@pytest.mark.parametrize('animal', ["dog", "cat"])
|
||||
def test_1(animal):
|
||||
assert animal in ('dog', 'cat')
|
||||
''')
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['* 2 passed *'])
|
||||
|
||||
def test_parametrize_all_indirects(self, testdir):
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.fixture()
|
||||
def animal(request):
|
||||
return request.param
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def echo(request):
|
||||
return request.param
|
||||
|
||||
@pytest.mark.parametrize('animal, echo', [("dog", 1), ("cat", 2)], indirect=True)
|
||||
def test_1(animal, echo):
|
||||
assert animal in ('dog', 'cat')
|
||||
assert echo in (1, 2, 3)
|
||||
|
||||
@pytest.mark.parametrize('animal, echo', [("fish", 3)], indirect=True)
|
||||
def test_2(animal, echo):
|
||||
assert animal == 'fish'
|
||||
assert echo in (1, 2, 3)
|
||||
''')
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['* 3 passed *'])
|
||||
|
||||
def test_parametrize_issue634(self, testdir):
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def foo(request):
|
||||
print('preparing foo-%d' % request.param)
|
||||
return 'foo-%d' % request.param
|
||||
|
||||
def test_one(foo):
|
||||
pass
|
||||
|
||||
def test_two(foo):
|
||||
pass
|
||||
|
||||
test_two.test_with = (2, 3)
|
||||
|
||||
def pytest_generate_tests(metafunc):
|
||||
params = (1, 2, 3, 4)
|
||||
if not 'foo' in metafunc.fixturenames:
|
||||
return
|
||||
|
||||
test_with = getattr(metafunc.function, 'test_with', None)
|
||||
if test_with:
|
||||
params = test_with
|
||||
metafunc.parametrize('foo', params, indirect=True)
|
||||
''')
|
||||
result = testdir.runpytest("-s")
|
||||
output = result.stdout.str()
|
||||
assert output.count('preparing foo-2') == 1
|
||||
assert output.count('preparing foo-3') == 1
|
||||
|
||||
|
||||
class TestMarkersWithParametrization:
|
||||
pytestmark = pytest.mark.issue308
|
||||
def test_simple_mark(self, testdir):
|
||||
|
||||
@@ -84,8 +84,32 @@ class TestImportHookInstallation:
|
||||
assert 0
|
||||
result.stdout.fnmatch_lines([expected])
|
||||
|
||||
@pytest.mark.parametrize('mode', ['str', 'list'])
|
||||
def test_pytest_plugins_rewrite_module_names(self, testdir, mode):
|
||||
"""Test that pluginmanager correct marks pytest_plugins variables
|
||||
for assertion rewriting if they are defined as plain strings or
|
||||
list of strings (#1888).
|
||||
"""
|
||||
plugins = '"ham"' if mode == 'str' else '["ham"]'
|
||||
contents = {
|
||||
'conftest.py': """
|
||||
pytest_plugins = {plugins}
|
||||
""".format(plugins=plugins),
|
||||
'ham.py': """
|
||||
import pytest
|
||||
""",
|
||||
'test_foo.py': """
|
||||
def test_foo(pytestconfig):
|
||||
assert 'ham' in pytestconfig.pluginmanager.rewrite_hook._must_rewrite
|
||||
""",
|
||||
}
|
||||
testdir.makepyfile(**contents)
|
||||
result = testdir.runpytest_subprocess('--assert=rewrite')
|
||||
assert result.ret == 0
|
||||
|
||||
@pytest.mark.parametrize('mode', ['plain', 'rewrite'])
|
||||
def test_installed_plugin_rewrite(self, testdir, mode):
|
||||
@pytest.mark.parametrize('plugin_state', ['development', 'installed'])
|
||||
def test_installed_plugin_rewrite(self, testdir, mode, plugin_state):
|
||||
# Make sure the hook is installed early enough so that plugins
|
||||
# installed via setuptools are re-written.
|
||||
testdir.tmpdir.join('hampkg').ensure(dir=1)
|
||||
@@ -112,13 +136,22 @@ class TestImportHookInstallation:
|
||||
'mainwrapper.py': """
|
||||
import pytest, pkg_resources
|
||||
|
||||
plugin_state = "{plugin_state}"
|
||||
|
||||
class DummyDistInfo:
|
||||
project_name = 'spam'
|
||||
version = '1.0'
|
||||
|
||||
def _get_metadata(self, name):
|
||||
return ['spamplugin.py,sha256=abc,123',
|
||||
'hampkg/__init__.py,sha256=abc,123']
|
||||
# 'RECORD' meta-data only available in installed plugins
|
||||
if name == 'RECORD' and plugin_state == "installed":
|
||||
return ['spamplugin.py,sha256=abc,123',
|
||||
'hampkg/__init__.py,sha256=abc,123']
|
||||
# 'SOURCES.txt' meta-data only available for plugins in development mode
|
||||
elif name == 'SOURCES.txt' and plugin_state == "development":
|
||||
return ['spamplugin.py',
|
||||
'hampkg/__init__.py']
|
||||
return []
|
||||
|
||||
class DummyEntryPoint:
|
||||
name = 'spam'
|
||||
@@ -136,7 +169,7 @@ class TestImportHookInstallation:
|
||||
|
||||
pkg_resources.iter_entry_points = iter_entry_points
|
||||
pytest.main()
|
||||
""",
|
||||
""".format(plugin_state=plugin_state),
|
||||
'test_foo.py': """
|
||||
def test(check_first):
|
||||
check_first([10, 30], 30)
|
||||
@@ -196,6 +229,12 @@ class TestImportHookInstallation:
|
||||
'>*assert l.pop() == 3*',
|
||||
'E*AssertionError'])
|
||||
|
||||
def test_register_assert_rewrite_checks_types(self):
|
||||
with pytest.raises(TypeError):
|
||||
pytest.register_assert_rewrite(['pytest_tests_internal_non_existing'])
|
||||
pytest.register_assert_rewrite('pytest_tests_internal_non_existing',
|
||||
'pytest_tests_internal_non_existing2')
|
||||
|
||||
|
||||
class TestBinReprIntegration:
|
||||
|
||||
@@ -816,3 +855,24 @@ def test_assert_indirect_tuple_no_warning(testdir):
|
||||
result = testdir.runpytest('-rw')
|
||||
output = '\n'.join(result.stdout.lines)
|
||||
assert 'WR1' not in output
|
||||
|
||||
def test_assert_with_unicode(monkeypatch, testdir):
|
||||
testdir.makepyfile(u"""
|
||||
# -*- coding: utf-8 -*-
|
||||
def test_unicode():
|
||||
assert u'유니코드' == u'Unicode'
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['*AssertionError*'])
|
||||
|
||||
def test_issue_1944(testdir):
|
||||
testdir.makepyfile("""
|
||||
def f():
|
||||
return
|
||||
|
||||
assert f() == 10
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*1 error*"])
|
||||
assert "AttributeError: 'Module' object has no attribute '_obj'" not in result.stdout.str()
|
||||
|
||||
|
||||
@@ -662,6 +662,28 @@ def test_dontreadfrominput():
|
||||
f.close() # just for completeness
|
||||
|
||||
|
||||
@pytest.mark.skipif('sys.version_info < (3,)', reason='python2 has no buffer')
|
||||
def test_dontreadfrominput_buffer_python3():
|
||||
from _pytest.capture import DontReadFromInput
|
||||
f = DontReadFromInput()
|
||||
fb = f.buffer
|
||||
assert not fb.isatty()
|
||||
pytest.raises(IOError, fb.read)
|
||||
pytest.raises(IOError, fb.readlines)
|
||||
pytest.raises(IOError, iter, fb)
|
||||
pytest.raises(ValueError, fb.fileno)
|
||||
f.close() # just for completeness
|
||||
|
||||
|
||||
@pytest.mark.skipif('sys.version_info >= (3,)', reason='python2 has no buffer')
|
||||
def test_dontreadfrominput_buffer_python2():
|
||||
from _pytest.capture import DontReadFromInput
|
||||
f = DontReadFromInput()
|
||||
with pytest.raises(AttributeError):
|
||||
f.buffer
|
||||
f.close() # just for completeness
|
||||
|
||||
|
||||
@pytest.yield_fixture
|
||||
def tmpfile(testdir):
|
||||
f = testdir.makepyfile("").open('wb+')
|
||||
|
||||
@@ -79,6 +79,25 @@ class TestPDB:
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
|
||||
def test_pdb_unittest_postmortem(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
import unittest
|
||||
class Blub(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
self.filename = None
|
||||
def test_false(self):
|
||||
self.filename = 'debug' + '.me'
|
||||
assert 0
|
||||
""")
|
||||
child = testdir.spawn_pytest("--pdb %s" % p1)
|
||||
child.expect('(Pdb)')
|
||||
child.sendline('p self.filename')
|
||||
child.sendeof()
|
||||
rest = child.read().decode("utf8")
|
||||
assert 'debug.me' in rest
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
|
||||
def test_pdb_interaction_capture(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
def test_1():
|
||||
|
||||
@@ -571,6 +571,19 @@ def test_importorskip_dev_module(monkeypatch):
|
||||
pytest.fail("spurious skip")
|
||||
|
||||
|
||||
def test_importorskip_module_level(testdir):
|
||||
"""importorskip must be able to skip entire modules when used at module level"""
|
||||
testdir.makepyfile('''
|
||||
import pytest
|
||||
foobarbaz = pytest.importorskip("foobarbaz")
|
||||
|
||||
def test_foo():
|
||||
pass
|
||||
''')
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(['*collected 0 items / 1 skipped*'])
|
||||
|
||||
|
||||
def test_pytest_cmdline_main(testdir):
|
||||
p = testdir.makepyfile("""
|
||||
import pytest
|
||||
|
||||
@@ -967,5 +967,5 @@ def test_module_level_skip_error(testdir):
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
"*Using @pytest.skip outside a test * is not allowed*"
|
||||
"*Using @pytest.skip outside of a test * is not allowed*"
|
||||
)
|
||||
|
||||
25
tox.ini
25
tox.ini
@@ -5,13 +5,13 @@ distshare={homedir}/.tox/distshare
|
||||
envlist=
|
||||
linting,py26,py27,py33,py34,py35,pypy,
|
||||
{py27,py35}-{pexpect,xdist,trial},
|
||||
py27-nobyte,doctesting,freeze
|
||||
py27-nobyte,doctesting,freeze,docs
|
||||
|
||||
[testenv]
|
||||
commands= pytest --lsof -rfsxX {posargs:testing}
|
||||
passenv = USER USERNAME
|
||||
deps=
|
||||
hypothesis
|
||||
deps=
|
||||
hypothesis>=3.5.2
|
||||
nose
|
||||
mock
|
||||
requests
|
||||
@@ -41,13 +41,13 @@ basepython = python2.7
|
||||
deps = flake8
|
||||
restructuredtext_lint
|
||||
commands = flake8 pytest.py _pytest testing
|
||||
rst-lint CHANGELOG.rst
|
||||
rst-lint CHANGELOG.rst HOWTORELEASE.rst
|
||||
|
||||
[testenv:py27-xdist]
|
||||
deps=pytest-xdist>=1.13
|
||||
mock
|
||||
nose
|
||||
hypothesis
|
||||
hypothesis>=3.5.2
|
||||
commands=
|
||||
pytest -n1 -rfsxX {posargs:testing}
|
||||
|
||||
@@ -71,8 +71,9 @@ commands=
|
||||
pytest -rfsxX test_pdb.py test_terminal.py test_unittest.py
|
||||
|
||||
[testenv:py27-nobyte]
|
||||
deps=pytest-xdist>=1.13
|
||||
hypothesis
|
||||
deps=
|
||||
pytest-xdist>=1.13
|
||||
hypothesis>=3.5.2
|
||||
distribute=true
|
||||
setenv=
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
@@ -93,15 +94,15 @@ commands=
|
||||
commands=pytest --doctest-modules _pytest
|
||||
deps=
|
||||
|
||||
[testenv:doc]
|
||||
[testenv:docs]
|
||||
basepython=python
|
||||
changedir=doc/en
|
||||
deps=sphinx
|
||||
PyYAML
|
||||
deps=
|
||||
sphinx
|
||||
PyYAML
|
||||
|
||||
commands=
|
||||
make clean
|
||||
make html
|
||||
sphinx-build -W -b html . _build
|
||||
|
||||
[testenv:doctesting]
|
||||
basepython = python
|
||||
|
||||
Reference in New Issue
Block a user