massive documentation refinements
This commit is contained in:
parent
7d495cc250
commit
c18cca9d54
|
@ -46,7 +46,7 @@ except ImportError:
|
||||||
args = [quote(arg) for arg in args]
|
args = [quote(arg) for arg in args]
|
||||||
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
|
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
|
||||||
|
|
||||||
DEFAULT_VERSION = "0.6.13"
|
DEFAULT_VERSION = "0.6.14"
|
||||||
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
|
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
|
||||||
SETUPTOOLS_FAKED_VERSION = "0.6c11"
|
SETUPTOOLS_FAKED_VERSION = "0.6c11"
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,9 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||||
|
|
||||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||||
|
|
||||||
|
regen:
|
||||||
|
COLUMNS=76 regendoc --update *.txt */*.txt
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
@echo "Please use \`make <target>' where <target> is one of"
|
||||||
@echo " html to make standalone HTML files"
|
@echo " html to make standalone HTML files"
|
||||||
|
|
|
@ -7,6 +7,7 @@ py.test reference documentation
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
customize.txt
|
||||||
assert.txt
|
assert.txt
|
||||||
funcargs.txt
|
funcargs.txt
|
||||||
xunit_setup.txt
|
xunit_setup.txt
|
||||||
|
@ -16,6 +17,6 @@ py.test reference documentation
|
||||||
skipping.txt
|
skipping.txt
|
||||||
mark.txt
|
mark.txt
|
||||||
recwarn.txt
|
recwarn.txt
|
||||||
doctest.txt
|
|
||||||
unittest.txt
|
unittest.txt
|
||||||
|
doctest.txt
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
Writing easy assertions in tests
|
Writing and reporting of assertions in tests
|
||||||
============================================
|
============================================
|
||||||
|
|
||||||
assert with the ``assert`` statement
|
assert with the ``assert`` statement
|
||||||
|
@ -21,7 +21,7 @@ assertion fails you will see the value of ``x``::
|
||||||
|
|
||||||
$ py.test test_assert1.py
|
$ py.test test_assert1.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_assert1.py
|
test path 1: test_assert1.py
|
||||||
|
|
||||||
test_assert1.py F
|
test_assert1.py F
|
||||||
|
@ -101,7 +101,7 @@ if you run this module::
|
||||||
|
|
||||||
$ py.test test_assert2.py
|
$ py.test test_assert2.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_assert2.py
|
test path 1: test_assert2.py
|
||||||
|
|
||||||
test_assert2.py F
|
test_assert2.py F
|
||||||
|
|
|
@ -11,9 +11,7 @@ Getting help on version, option names, environment vars
|
||||||
|
|
||||||
py.test --version # shows where pytest was imported from
|
py.test --version # shows where pytest was imported from
|
||||||
py.test --funcargs # show available builtin function arguments
|
py.test --funcargs # show available builtin function arguments
|
||||||
py.test --help-config # show configuration values
|
py.test -h | --help # show help on command line and config file options
|
||||||
|
|
||||||
py.test -h | --help # show help
|
|
||||||
|
|
||||||
|
|
||||||
Stopping after the first (or N) failures
|
Stopping after the first (or N) failures
|
||||||
|
|
|
@ -25,7 +25,8 @@ import sys, os
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
|
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo',
|
||||||
|
'sphinx.ext.intersphinx', 'sphinx.ext.viewcode']
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
Contact and Communication points
|
|
||||||
|
.. _`contact channels`:
|
||||||
|
|
||||||
|
Contact channels
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
- `py-dev developers list`_ announcements and discussions.
|
- `new issue tracker`_ to report bugs or suggest features.
|
||||||
|
See also the `old issue tracker`_ but don't submit bugs there.
|
||||||
|
|
||||||
|
- `Testing In Python`_: a mailing list for Python testing tools and discussion.
|
||||||
|
|
||||||
|
- `py-dev developers list`_ pytest specific announcements and discussions.
|
||||||
|
|
||||||
- #pylib on irc.freenode.net IRC channel for random questions.
|
- #pylib on irc.freenode.net IRC channel for random questions.
|
||||||
|
|
||||||
|
|
||||||
- `tetamap`_: Holger Krekel's blog, often about testing and py.test related news.
|
- `tetamap`_: Holger Krekel's blog, often about testing and py.test related news.
|
||||||
|
- `commit mailing list`_
|
||||||
- `Testing In Python`_: a mailing list for testing tools and discussion.
|
|
||||||
|
|
||||||
- `commit mailing list`_ or `@pylibcommit`_ to follow development commits,
|
|
||||||
|
|
||||||
- `bitbucket issue tracker`_ use this bitbucket issue tracker to report
|
|
||||||
bugs or request features.
|
|
||||||
|
|
||||||
- `merlinux.eu`_ offers on-site teaching and consulting services.
|
- `merlinux.eu`_ offers on-site teaching and consulting services.
|
||||||
|
|
||||||
.. _`bitbucket issue tracker`: http://bitbucket.org/hpk42/py-trunk/issues/
|
.. _`new issue tracker`: http://bitbucket.org/hpk42/pytest/issues/
|
||||||
|
.. _`old issue tracker`: http://bitbucket.org/hpk42/py-trunk/issues/
|
||||||
|
|
||||||
.. _`merlinux.eu`: http://merlinux.eu
|
.. _`merlinux.eu`: http://merlinux.eu
|
||||||
|
|
||||||
|
@ -28,20 +30,8 @@ Contact and Communication points
|
||||||
.. _`@pylibcommit`: http://twitter.com/pylibcommit
|
.. _`@pylibcommit`: http://twitter.com/pylibcommit
|
||||||
|
|
||||||
|
|
||||||
..
|
|
||||||
get an account on codespeak
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
codespeak_ is where the subversion repository is hosted. If you know
|
|
||||||
someone who is active on codespeak already or you are otherwise known in
|
|
||||||
the community (see also: FOAF_) you will get access. But even if
|
|
||||||
you are new to the python developer community please come to the IRC
|
|
||||||
or the mailing list and ask questions, get involved.
|
|
||||||
|
|
||||||
.. _`Testing in Python`: http://lists.idyll.org/listinfo/testing-in-python
|
.. _`Testing in Python`: http://lists.idyll.org/listinfo/testing-in-python
|
||||||
.. _FOAF: http://en.wikipedia.org/wiki/FOAF
|
.. _FOAF: http://en.wikipedia.org/wiki/FOAF
|
||||||
.. _us: http://codespeak.net/mailman/listinfo/py-dev
|
|
||||||
.. _codespeak: http://codespeak.net/
|
|
||||||
.. _`py-dev`:
|
.. _`py-dev`:
|
||||||
.. _`development mailing list`:
|
.. _`development mailing list`:
|
||||||
.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev
|
.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
================================================
|
|
||||||
Customizing and Extending py.test
|
|
||||||
================================================
|
|
||||||
|
|
||||||
basic test configuration
|
basic test configuration
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
|
@ -58,309 +54,4 @@ builtin configuration file options
|
||||||
py.test --maxfail=2 -rf test_hello.py
|
py.test --maxfail=2 -rf test_hello.py
|
||||||
|
|
||||||
.. _`function arguments`: funcargs.html
|
.. _`function arguments`: funcargs.html
|
||||||
.. _`extensions`:
|
|
||||||
|
|
||||||
Plugin basics and project configuration
|
|
||||||
=============================================
|
|
||||||
|
|
||||||
.. _`local plugin`:
|
|
||||||
|
|
||||||
py.test implements all aspects of its functionality by calling `well specified
|
|
||||||
hooks`_. Hook functions are discovered in :file:`conftest.py` files or in
|
|
||||||
`named plugins`_. :file:`conftest.py` files are useful for keeping test
|
|
||||||
extensions and customizations close to test code.
|
|
||||||
|
|
||||||
local conftest.py plugins
|
|
||||||
--------------------------------------------------------------
|
|
||||||
|
|
||||||
local ``conftest.py`` plugins contain directory-specific hook implemenations. Its contained runtest- and collection- related hooks are called when collecting or running tests in files or directories next to or below the ``conftest.py``
|
|
||||||
file. Example: Assume the following layout and content of files::
|
|
||||||
|
|
||||||
a/conftest.py:
|
|
||||||
def pytest_runtest_setup(item):
|
|
||||||
print ("setting up", item)
|
|
||||||
|
|
||||||
a/test_in_subdir.py:
|
|
||||||
def test_sub():
|
|
||||||
pass
|
|
||||||
|
|
||||||
test_flat.py:
|
|
||||||
def test_flat():
|
|
||||||
pass
|
|
||||||
|
|
||||||
Here is how you might run it::
|
|
||||||
|
|
||||||
py.test test_flat.py # will not show "setting up"
|
|
||||||
py.test a/test_sub.py # will show "setting up"
|
|
||||||
|
|
||||||
``py.test`` loads all ``conftest.py`` files upwards from the command
|
|
||||||
line file arguments. It usually performs look up right-to-left, i.e.
|
|
||||||
the hooks in "closer" conftest files will be called earlier than further
|
|
||||||
away ones. This means you can even have a ``conftest.py`` file in your home
|
|
||||||
directory to customize test functionality globally for all of your projects.
|
|
||||||
|
|
||||||
.. Note::
|
|
||||||
If you have ``conftest.py`` files which do not reside in a
|
|
||||||
python package directory (i.e. one containing an ``__init__.py``) then
|
|
||||||
"import conftest" can be ambigous because there might be other
|
|
||||||
``conftest.py`` files as well on your PYTHONPATH or ``sys.path``.
|
|
||||||
It is good practise for projects to put ``conftest.py`` within a package
|
|
||||||
scope or to never import anything from the conftest.py file.
|
|
||||||
|
|
||||||
.. _`named plugins`: plugin/index.html
|
|
||||||
|
|
||||||
|
|
||||||
Plugin discovery at tool startup
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
py.test loads plugin modules at tool startup in the following way:
|
|
||||||
|
|
||||||
* by loading all plugins registered through `setuptools entry points`_.
|
|
||||||
|
|
||||||
* by pre-scanning the command line for the ``-p name`` option
|
|
||||||
and loading the specified plugin before actual command line parsing.
|
|
||||||
|
|
||||||
* by loading all :file:`conftest.py` files as inferred by the command line
|
|
||||||
invocation (test files and all of its *parent* directories).
|
|
||||||
Note that ``conftest.py`` files from *sub* directories are by default
|
|
||||||
not loaded at tool startup.
|
|
||||||
|
|
||||||
* by recursively loading all plugins specified by the
|
|
||||||
``pytest_plugins`` variable in ``conftest.py`` files
|
|
||||||
|
|
||||||
Requiring/Loading plugins in a test module or conftest file
|
|
||||||
-------------------------------------------------------------
|
|
||||||
|
|
||||||
You can require plugins in a test module or a conftest file like this::
|
|
||||||
|
|
||||||
pytest_plugins = "name1", "name2",
|
|
||||||
|
|
||||||
When the test module or conftest plugin is loaded the specified plugins
|
|
||||||
will be loaded as well. You can also use dotted path like this::
|
|
||||||
|
|
||||||
pytest_plugins = "myapp.testsupport.myplugin"
|
|
||||||
|
|
||||||
which will import the specified module as a py.test plugin.
|
|
||||||
|
|
||||||
.. _`setuptools entry points`:
|
|
||||||
.. _registered:
|
|
||||||
|
|
||||||
Writing setuptools-registered plugins
|
|
||||||
------------------------------------------------------
|
|
||||||
|
|
||||||
.. _`Distribute`: http://pypi.python.org/pypi/distribute
|
|
||||||
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
|
|
||||||
|
|
||||||
If you want to make your plugin publically available, you
|
|
||||||
can use `setuptools`_ or `Distribute`_ which both allow
|
|
||||||
to register an entry point. ``py.test`` will register
|
|
||||||
all objects with the ``pytest11`` entry point.
|
|
||||||
To make your plugin available you may insert the following
|
|
||||||
lines in your setuptools/distribute-based setup-invocation:
|
|
||||||
|
|
||||||
.. sourcecode:: python
|
|
||||||
|
|
||||||
# sample ./setup.py file
|
|
||||||
from setuptools import setup
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="myproject",
|
|
||||||
packages = ['myproject']
|
|
||||||
|
|
||||||
# the following makes a plugin available to py.test
|
|
||||||
entry_points = {
|
|
||||||
'pytest11': [
|
|
||||||
'name_of_plugin = myproject.pluginmodule',
|
|
||||||
]
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
If a package is installed with this setup, py.test will load
|
|
||||||
``myproject.pluginmodule`` under the ``name_of_plugin`` name
|
|
||||||
and use it as a plugin.
|
|
||||||
|
|
||||||
Accessing another plugin by name
|
|
||||||
--------------------------------------------
|
|
||||||
|
|
||||||
If a plugin wants to collaborate with code from
|
|
||||||
another plugin it can obtain a reference through
|
|
||||||
the plugin manager like this:
|
|
||||||
|
|
||||||
.. sourcecode:: python
|
|
||||||
|
|
||||||
plugin = config.pluginmanager.getplugin("name_of_plugin")
|
|
||||||
|
|
||||||
If you want to look at the names of existing plugins, use
|
|
||||||
the ``--traceconfig`` option.
|
|
||||||
|
|
||||||
.. _`well specified hooks`:
|
|
||||||
|
|
||||||
py.test hook reference
|
|
||||||
====================================
|
|
||||||
|
|
||||||
hook specification and validation
|
|
||||||
-----------------------------------------
|
|
||||||
|
|
||||||
py.test calls hook functions to implement initialization, running,
|
|
||||||
test execution and reporting. When py.test loads a plugin it validates
|
|
||||||
that all hook functions conform to their respective hook specification.
|
|
||||||
Each hook function name and its argument names need to match a hook
|
|
||||||
specification exactly but it is allowed for a hook function to accept
|
|
||||||
*less* parameters than specified. If you mistype argument names or the
|
|
||||||
hook name itself you get useful errors.
|
|
||||||
|
|
||||||
initialisation, command line and configuration hooks
|
|
||||||
--------------------------------------------------------------------
|
|
||||||
|
|
||||||
.. currentmodule:: pytest.hookspec
|
|
||||||
|
|
||||||
.. autofunction:: pytest_cmdline_parse
|
|
||||||
.. autofunction:: pytest_namespace
|
|
||||||
.. autofunction:: pytest_addoption
|
|
||||||
.. autofunction:: pytest_cmdline_main
|
|
||||||
.. autofunction:: pytest_configure
|
|
||||||
.. autofunction:: pytest_unconfigure
|
|
||||||
|
|
||||||
generic "runtest" hooks
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
All all runtest related hooks receive a :py:class:`pytest.collect.Item` object.
|
|
||||||
|
|
||||||
.. autofunction:: pytest_runtest_protocol
|
|
||||||
.. autofunction:: pytest_runtest_setup
|
|
||||||
.. autofunction:: pytest_runtest_call
|
|
||||||
.. autofunction:: pytest_runtest_teardown
|
|
||||||
.. autofunction:: pytest_runtest_makereport
|
|
||||||
|
|
||||||
For deeper understanding you may look at the default implementation of
|
|
||||||
these hooks in :py:mod:`pytest.plugin.runner` and maybe also
|
|
||||||
in :py:mod:`pytest.plugin.pdb` which intercepts creation
|
|
||||||
of reports in order to drop to interactive debugging.
|
|
||||||
|
|
||||||
The :py:mod:`pytest.plugin.terminal` reported specifically uses
|
|
||||||
the reporting hook to print information about a test run.
|
|
||||||
|
|
||||||
collection hooks
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
py.test calls the following hooks for collecting files and directories:
|
|
||||||
|
|
||||||
.. autofunction:: pytest_ignore_collect
|
|
||||||
.. autofunction:: pytest_collect_directory
|
|
||||||
.. autofunction:: pytest_collect_file
|
|
||||||
|
|
||||||
For influencing the collection of objects in Python modules
|
|
||||||
you can use the following hook:
|
|
||||||
|
|
||||||
.. autofunction:: pytest_pycollect_makeitem
|
|
||||||
|
|
||||||
|
|
||||||
reporting hooks
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
Collection related reporting hooks:
|
|
||||||
|
|
||||||
.. autofunction: pytest_collectstart
|
|
||||||
.. autofunction: pytest_itemcollected
|
|
||||||
.. autofunction: pytest_collectreport
|
|
||||||
.. autofunction: pytest_deselected
|
|
||||||
|
|
||||||
And here is the central hook for reporting about
|
|
||||||
test execution:
|
|
||||||
|
|
||||||
.. autofunction: pytest_runtest_logreport
|
|
||||||
|
|
||||||
The test collection tree
|
|
||||||
======================================================
|
|
||||||
|
|
||||||
|
|
||||||
Default filesystem test discovery
|
|
||||||
-----------------------------------------------
|
|
||||||
|
|
||||||
Test collection starts from specified paths or from the current
|
|
||||||
directory. All tests are collected ahead of running the first test.
|
|
||||||
(This used to be different in earlier versions of ``py.test`` where
|
|
||||||
collection and running was interweaved which made test randomization
|
|
||||||
and distributed testing harder).
|
|
||||||
|
|
||||||
Collection nodes which have children are called "Collectors" and otherwise
|
|
||||||
they are called "Items" or "test items". Here is an example of such a
|
|
||||||
tree::
|
|
||||||
|
|
||||||
example $ py.test --collectonly test_collectonly.py
|
|
||||||
<Directory 'example'>
|
|
||||||
<Module 'test_collectonly.py'>
|
|
||||||
<Function 'test_function'>
|
|
||||||
<Class 'TestClass'>
|
|
||||||
<Instance '()'>
|
|
||||||
<Function 'test_method'>
|
|
||||||
<Function 'test_anothermethod'>
|
|
||||||
|
|
||||||
By default all directories not starting with a dot are traversed,
|
|
||||||
looking for ``test_*.py`` and ``*_test.py`` files. Those Python
|
|
||||||
files are imported under their `package name`_.
|
|
||||||
|
|
||||||
The Module collector looks for test functions
|
|
||||||
and test classes and methods. Test functions and methods
|
|
||||||
are prefixed ``test`` by default. Test classes must
|
|
||||||
start with a capitalized ``Test`` prefix.
|
|
||||||
|
|
||||||
Customizing error messages
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
On test and collection nodes ``py.test`` will invoke
|
|
||||||
the ``node.repr_failure(excinfo)`` function which
|
|
||||||
you may override and make it return an error
|
|
||||||
representation string of your choice. It
|
|
||||||
will be reported as a (red) string.
|
|
||||||
|
|
||||||
.. _`package name`:
|
|
||||||
|
|
||||||
constructing the package name for test modules
|
|
||||||
-------------------------------------------------
|
|
||||||
|
|
||||||
Test modules are imported under their fully qualified
|
|
||||||
name. Given a filesystem ``fspath`` it is constructed as follows:
|
|
||||||
|
|
||||||
* walk the directories up to the last one that contains
|
|
||||||
an ``__init__.py`` file.
|
|
||||||
|
|
||||||
* perform ``sys.path.insert(0, basedir)``.
|
|
||||||
|
|
||||||
* import the root package as ``root``
|
|
||||||
|
|
||||||
Reference of important objects involved in hooks
|
|
||||||
===========================================================
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.config.Config
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.config.Parser
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.session.Node(name, parent)
|
|
||||||
:members:
|
|
||||||
|
|
||||||
..
|
|
||||||
.. autoclass:: pytest.plugin.session.File(fspath, parent)
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.session.Item(name, parent)
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.python.Module(name, parent)
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.python.Class(name, parent)
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.python.Function(name, parent)
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.runner.CallInfo
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: pytest.plugin.runner.TestReport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
Test collection and discovery
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
.. _`discovered`:
|
||||||
|
|
||||||
|
Default filesystem test discovery
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Test collection starts from paths specified at the command line or from
|
||||||
|
the current directory. Tests are collected ahead of running the first test.
|
||||||
|
(This used to be different in earlier versions of ``py.test`` where
|
||||||
|
collection and running was interweaved which made test randomization
|
||||||
|
and distributed testing harder).
|
||||||
|
|
||||||
|
Collection nodes which have children are called "Collectors" and otherwise
|
||||||
|
they are called "Items" or "test items". Here is an example of such a
|
||||||
|
tree::
|
||||||
|
|
||||||
|
example $ py.test --collectonly test_collectonly.py
|
||||||
|
<Directory 'example'>
|
||||||
|
<Module 'test_collectonly.py'>
|
||||||
|
<Function 'test_function'>
|
||||||
|
<Class 'TestClass'>
|
||||||
|
<Instance '()'>
|
||||||
|
<Function 'test_method'>
|
||||||
|
<Function 'test_anothermethod'>
|
||||||
|
|
||||||
|
By default all directories not starting with a dot are traversed,
|
||||||
|
looking for ``test_*.py`` and ``*_test.py`` files. Those Python
|
||||||
|
files are imported under their `package name`_.
|
||||||
|
|
||||||
|
The Module collector looks for test functions
|
||||||
|
and test classes and methods. Test functions and methods
|
||||||
|
are prefixed ``test`` by default. Test classes must
|
||||||
|
start with a capitalized ``Test`` prefix.
|
||||||
|
|
||||||
|
Customizing error messages
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
On test and collection nodes ``py.test`` will invoke
|
||||||
|
the ``node.repr_failure(excinfo)`` function which
|
||||||
|
you may override and make it return an error
|
||||||
|
representation string of your choice. It
|
||||||
|
will be reported as a (red) string.
|
|
@ -44,11 +44,7 @@ then you can just invoke ``py.test`` without command line options::
|
||||||
|
|
||||||
$ py.test
|
$ py.test
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: /tmp/doc-exec-288
|
test path 1: /tmp/doc-exec-197
|
||||||
|
|
||||||
conftest.py .
|
============================= in 0.00 seconds =============================
|
||||||
example.rst .
|
|
||||||
mymodule.py .
|
|
||||||
|
|
||||||
========================= 3 passed in 0.01 seconds =========================
|
|
||||||
|
|
|
@ -36,12 +36,12 @@ and when running it will see a skipped "slow" test::
|
||||||
|
|
||||||
$ py.test test_module.py -rs # "-rs" means report on the little 's'
|
$ py.test test_module.py -rs # "-rs" means report on the little 's'
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_module.py
|
test path 1: test_module.py
|
||||||
|
|
||||||
test_module.py .s
|
test_module.py .s
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
SKIP [1] /tmp/doc-exec-12/conftest.py:9: 'need --runslow option to run'
|
SKIP [1] /tmp/doc-exec-195/conftest.py:9: 'need --runslow option to run'
|
||||||
|
|
||||||
=================== 1 passed, 1 skipped in 0.02 seconds ====================
|
=================== 1 passed, 1 skipped in 0.02 seconds ====================
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ Or run it including the ``slow`` marked test::
|
||||||
|
|
||||||
$ py.test test_module.py --runslow
|
$ py.test test_module.py --runslow
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_module.py
|
test path 1: test_module.py
|
||||||
|
|
||||||
test_module.py ..
|
test_module.py ..
|
||||||
|
|
|
@ -49,7 +49,7 @@ You can now run the test::
|
||||||
|
|
||||||
$ py.test test_sample.py
|
$ py.test test_sample.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_sample.py
|
test path 1: test_sample.py
|
||||||
|
|
||||||
test_sample.py F
|
test_sample.py F
|
||||||
|
@ -57,7 +57,7 @@ You can now run the test::
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_______________________________ test_answer ________________________________
|
_______________________________ test_answer ________________________________
|
||||||
|
|
||||||
mysetup = <conftest.MySetup instance at 0x1a09f38>
|
mysetup = <conftest.MySetup instance at 0x2809a70>
|
||||||
|
|
||||||
def test_answer(mysetup):
|
def test_answer(mysetup):
|
||||||
app = mysetup.myapp()
|
app = mysetup.myapp()
|
||||||
|
@ -122,12 +122,12 @@ Running it yields::
|
||||||
|
|
||||||
$ py.test test_ssh.py -rs
|
$ py.test test_ssh.py -rs
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_ssh.py
|
test path 1: test_ssh.py
|
||||||
|
|
||||||
test_ssh.py s
|
test_ssh.py s
|
||||||
========================= short test summary info ==========================
|
========================= short test summary info ==========================
|
||||||
SKIP [1] /tmp/doc-exec-9/conftest.py:22: 'specify ssh host with --ssh'
|
SKIP [1] /tmp/doc-exec-198/conftest.py:22: 'specify ssh host with --ssh'
|
||||||
|
|
||||||
======================== 1 skipped in 0.02 seconds =========================
|
======================== 1 skipped in 0.02 seconds =========================
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ now execute the test specification::
|
||||||
|
|
||||||
nonpython $ py.test test_simple.yml
|
nonpython $ py.test test_simple.yml
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev10
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: /home/hpk/p/pytest/doc/example/nonpython
|
test path 1: test_simple.yml
|
||||||
|
|
||||||
test_simple.yml .F
|
test_simple.yml .F
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ now execute the test specification::
|
||||||
usecase execution failed
|
usecase execution failed
|
||||||
spec failed: 'some': 'other'
|
spec failed: 'some': 'other'
|
||||||
no further details known at this point.
|
no further details known at this point.
|
||||||
==================== 1 failed, 1 passed in 0.06 seconds ====================
|
==================== 1 failed, 1 passed in 0.37 seconds ====================
|
||||||
|
|
||||||
You get one dot for the passing ``sub1: sub1`` check and one failure.
|
You get one dot for the passing ``sub1: sub1`` check and one failure.
|
||||||
Obviously in the above ``conftest.py`` you'll want to implement a more
|
Obviously in the above ``conftest.py`` you'll want to implement a more
|
||||||
|
@ -45,7 +45,7 @@ reporting in ``verbose`` mode::
|
||||||
|
|
||||||
nonpython $ py.test -v
|
nonpython $ py.test -v
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev10 -- /home/hpk/venv/0/bin/python
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17 -- /home/hpk/venv/0/bin/python
|
||||||
test path 1: /home/hpk/p/pytest/doc/example/nonpython
|
test path 1: /home/hpk/p/pytest/doc/example/nonpython
|
||||||
|
|
||||||
test_simple.yml:1: usecase: ok PASSED
|
test_simple.yml:1: usecase: ok PASSED
|
||||||
|
@ -56,7 +56,7 @@ reporting in ``verbose`` mode::
|
||||||
usecase execution failed
|
usecase execution failed
|
||||||
spec failed: 'some': 'other'
|
spec failed: 'some': 'other'
|
||||||
no further details known at this point.
|
no further details known at this point.
|
||||||
==================== 1 failed, 1 passed in 0.06 seconds ====================
|
==================== 1 failed, 1 passed in 0.07 seconds ====================
|
||||||
|
|
||||||
While developing your custom test collection and execution it's also
|
While developing your custom test collection and execution it's also
|
||||||
interesting to just look at the collection tree::
|
interesting to just look at the collection tree::
|
||||||
|
|
94
doc/faq.txt
94
doc/faq.txt
|
@ -1,87 +1,56 @@
|
||||||
Frequent Issues and Questions
|
Some Issues and Questions
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
.. _`installation issues`:
|
.. note::
|
||||||
|
|
||||||
Installation issues
|
If you don't find an answer here, checkout the :ref:`contact channels`
|
||||||
------------------------------
|
to get help.
|
||||||
|
|
||||||
easy_install or pip not found?
|
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
Consult distribute_ to install the ``easy_install`` tool on your machine.
|
|
||||||
You may also use the original but somewhat older `setuptools`_ project
|
|
||||||
although we generally recommend to use ``distribute`` because it contains
|
|
||||||
more bug fixes and also works for Python3.
|
|
||||||
|
|
||||||
For Python2 you can also consult pip_ for the popular ``pip`` tool.
|
|
||||||
|
|
||||||
However, If you want to install on Python3 you need to use Distribute_ which
|
|
||||||
provides the ``easy_install`` utility.
|
|
||||||
|
|
||||||
|
|
||||||
py.test not found on Windows despite installation?
|
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
||||||
|
|
||||||
.. _`Python for Windows`: http://www.imladris.com/Scripts/PythonForWindows.html
|
|
||||||
|
|
||||||
|
|
||||||
- **Windows**: If "easy_install" or "py.test" are not found
|
|
||||||
please see here for preparing your environment for running
|
|
||||||
command line tools: `Python for Windows`_. You may alternatively
|
|
||||||
use an `ActivePython install`_ which makes command line tools
|
|
||||||
automatically available under Windows.
|
|
||||||
|
|
||||||
.. _`ActivePython install`: http://www.activestate.com/activepython/downloads
|
|
||||||
|
|
||||||
.. _`Jython does not create command line launchers`: http://bugs.jython.org/issue1491
|
|
||||||
|
|
||||||
- **Jython2.5.1 on Windows XP**: `Jython does not create command line launchers`_
|
|
||||||
so ``py.test`` will not work correctly. You may install py.test on
|
|
||||||
CPython and type ``py.test --genscript=mytest`` and then use
|
|
||||||
``jython mytest`` to run py.test for your tests to run in Jython.
|
|
||||||
|
|
||||||
On naming, nosetests, licensing and magic XXX
|
On naming, nosetests, licensing and magic XXX
|
||||||
------------------------------------------------
|
------------------------------------------------
|
||||||
|
|
||||||
Why the ``py.test`` naming, why not ``pytest``?
|
Why a ``py.test`` instead of a ``pytest`` command?
|
||||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
XXX
|
Some historic, some practical reasons: ``py.test`` used to be part of
|
||||||
|
the ``py`` package which provided several developer utitilities,
|
||||||
because of TAB-completion under Bash/Shells. If you hit
|
all starting with ``py.<TAB>``, providing nice TAB-completion. If
|
||||||
``py.<TAB>`` you'll get a list of available development
|
you install ``pip install pycmd`` you get these tools from a separate
|
||||||
tools that all share the ``py.`` prefix. Another motivation
|
package. These days the command line tool could be ``pytest``
|
||||||
was to unify the package ("py.test") and tool filename.
|
but then many people have gotten used to the old name and there
|
||||||
|
also is another tool with this same which would lead to some clashes.
|
||||||
|
|
||||||
What's py.test's relation to ``nosetests``?
|
What's py.test's relation to ``nosetests``?
|
||||||
+++++++++++++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
py.test and nose_ share basic philosophy when it comes
|
py.test and nose_ share basic philosophy when it comes
|
||||||
to running Python tests. In fact,
|
to running Python tests. In fact, you can run many tests
|
||||||
with py.test-1.1.0 it is ever easier to run many test suites
|
written for unittest or nose with py.test. nose_ was originally created
|
||||||
that currently work with ``nosetests``. nose_ was created
|
|
||||||
as a clone of ``py.test`` when py.test was in the ``0.8`` release
|
as a clone of ``py.test`` when py.test was in the ``0.8`` release
|
||||||
cycle so some of the newer features_ introduced with py.test-1.0
|
cycle.
|
||||||
and py.test-1.1 have no counterpart in nose_.
|
|
||||||
|
|
||||||
.. _features: test/features.html
|
.. _features: test/features.html
|
||||||
.. _apipkg: http://pypi.python.org/pypi/apipkg
|
|
||||||
|
|
||||||
|
|
||||||
What's this "magic" with py.test?
|
What's this "magic" with py.test?
|
||||||
++++++++++++++++++++++++++++++++++++++++++
|
++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
Around 2007 it was claimed that py.test was magic implementation
|
Around 2007 (version ``0.8``) some several people claimed that py.test
|
||||||
wise XXX. It has been refactored.
|
was using too much "magic". It has been refactored a lot. It is today
|
||||||
|
probably one of the smallest, most universally runnable and most
|
||||||
|
customizable testing frameworks for Python. It remains true
|
||||||
|
that ``py.test`` uses metaprogramming techniques, i.e. it views
|
||||||
|
test code similar to how compilers view programs, using a
|
||||||
|
somewhat abstract internal model.
|
||||||
|
|
||||||
* when an ``assert`` statement fails, py.test re-interprets the expression
|
It's also true that the no-boilerplate testing is implemented by making
|
||||||
to show intermediate values if a test fails. If your expression
|
use of the Python assert statement through "re-interpretation":
|
||||||
has side effects the intermediate values may not be the same, obfuscating
|
When an ``assert`` statement fails, py.test re-interprets the expression
|
||||||
the initial error (this is also explained at the command line if it happens).
|
to show intermediate values if a test fails. If your expression
|
||||||
``py.test --no-assert`` turns off assert re-intepretation.
|
has side effects the intermediate values may not be the same, obfuscating
|
||||||
Sidenote: it is good practise to avoid asserts with side effects.
|
the initial error (this is also explained at the command line if it happens).
|
||||||
|
``py.test --no-assert`` turns off assert re-intepretation.
|
||||||
|
Sidenote: it is good practise to avoid asserts with side effects.
|
||||||
|
|
||||||
.. _`py namespaces`: index.html
|
.. _`py namespaces`: index.html
|
||||||
.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py
|
.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py
|
||||||
|
@ -92,10 +61,9 @@ function arguments, parametrized tests and setup
|
||||||
|
|
||||||
.. _funcargs: test/funcargs.html
|
.. _funcargs: test/funcargs.html
|
||||||
|
|
||||||
Is using funcarg- versus xUnit-based setup a style question?
|
Is using funcarg- versus xUnit setup a style question?
|
||||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
XXX
|
|
||||||
For simple applications and for people experienced with nose_ or
|
For simple applications and for people experienced with nose_ or
|
||||||
unittest-style test setup using `xUnit style setup`_
|
unittest-style test setup using `xUnit style setup`_
|
||||||
feels natural. For larger test suites, parametrized testing
|
feels natural. For larger test suites, parametrized testing
|
||||||
|
|
|
@ -4,9 +4,11 @@ py.test Features
|
||||||
no-boilerplate testing with Python
|
no-boilerplate testing with Python
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
- automatic customizable Python test discovery
|
- automatic, fully customizable Python test discovery
|
||||||
|
- :pep:`8` consistent testing style
|
||||||
|
- allows simple test functions
|
||||||
|
- ``assert`` statement for your assertions
|
||||||
- powerful parametrization of test functions
|
- powerful parametrization of test functions
|
||||||
- use the ``assert`` statement for your assertions
|
|
||||||
- rely on powerful traceback and assertion reporting
|
- rely on powerful traceback and assertion reporting
|
||||||
- use ``print`` or ``pdb`` debugging on failures
|
- use ``print`` or ``pdb`` debugging on failures
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ Running the test looks like this::
|
||||||
|
|
||||||
$ py.test test_simplefactory.py
|
$ py.test test_simplefactory.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_simplefactory.py
|
test path 1: test_simplefactory.py
|
||||||
|
|
||||||
test_simplefactory.py F
|
test_simplefactory.py F
|
||||||
|
@ -133,7 +133,7 @@ Running this::
|
||||||
|
|
||||||
$ py.test test_example.py
|
$ py.test test_example.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_example.py
|
test path 1: test_example.py
|
||||||
|
|
||||||
test_example.py .........F
|
test_example.py .........F
|
||||||
|
@ -154,7 +154,7 @@ Note that the ``pytest_generate_tests(metafunc)`` hook is called during
|
||||||
the test collection phase. You can have a look at it with this::
|
the test collection phase. You can have a look at it with this::
|
||||||
|
|
||||||
$ py.test --collectonly test_example.py
|
$ py.test --collectonly test_example.py
|
||||||
<Directory 'doc-exec-341'>
|
<Directory 'doc-exec-167'>
|
||||||
<Module 'test_example.py'>
|
<Module 'test_example.py'>
|
||||||
<Function 'test_func[0]'>
|
<Function 'test_func[0]'>
|
||||||
<Function 'test_func[1]'>
|
<Function 'test_func[1]'>
|
||||||
|
@ -171,14 +171,31 @@ If you want to select only the run with the value ``7`` you could do::
|
||||||
|
|
||||||
$ py.test -v -k 7 test_example.py # or -k test_func[7]
|
$ py.test -v -k 7 test_example.py # or -k test_func[7]
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0 -- /home/hpk/venv/0/bin/python
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17 -- /home/hpk/venv/0/bin/python
|
||||||
test path 1: test_example.py
|
test path 1: test_example.py
|
||||||
|
|
||||||
|
test_example.py:6: test_func[0] PASSED
|
||||||
|
test_example.py:6: test_func[1] PASSED
|
||||||
|
test_example.py:6: test_func[2] PASSED
|
||||||
|
test_example.py:6: test_func[3] PASSED
|
||||||
|
test_example.py:6: test_func[4] PASSED
|
||||||
|
test_example.py:6: test_func[5] PASSED
|
||||||
|
test_example.py:6: test_func[6] PASSED
|
||||||
test_example.py:6: test_func[7] PASSED
|
test_example.py:6: test_func[7] PASSED
|
||||||
|
test_example.py:6: test_func[8] PASSED
|
||||||
|
test_example.py:6: test_func[9] FAILED
|
||||||
|
|
||||||
======================== 9 tests deselected by '7' =========================
|
================================= FAILURES =================================
|
||||||
================== 1 passed, 9 deselected in 0.01 seconds ==================
|
_______________________________ test_func[9] _______________________________
|
||||||
|
|
||||||
|
numiter = 9
|
||||||
|
|
||||||
|
def test_func(numiter):
|
||||||
|
> assert numiter < 9
|
||||||
|
E assert 9 < 9
|
||||||
|
|
||||||
|
test_example.py:7: AssertionError
|
||||||
|
==================== 1 failed, 9 passed in 0.04 seconds ====================
|
||||||
|
|
||||||
.. _`metafunc object`:
|
.. _`metafunc object`:
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Getting Started
|
Installation and Getting Started
|
||||||
===================================
|
===================================
|
||||||
|
|
||||||
.. _`easy_install`:
|
.. _`easy_install`:
|
||||||
|
@ -20,7 +20,7 @@ To check your installation has installed the correct version::
|
||||||
If you get an error, checkout :ref:`installation issues`.
|
If you get an error, checkout :ref:`installation issues`.
|
||||||
|
|
||||||
|
|
||||||
Writing a simple test function with an assertion
|
Our first test run
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
|
|
||||||
Let's create a small file with a test function testing a function
|
Let's create a small file with a test function testing a function
|
||||||
|
@ -32,17 +32,17 @@ computes a certain value::
|
||||||
def test_answer():
|
def test_answer():
|
||||||
assert func(3) == 5
|
assert func(3) == 5
|
||||||
|
|
||||||
Now you can execute the test function::
|
You can execute the test function::
|
||||||
|
|
||||||
$ py.test test_sample.py
|
$ py.test test_sample.py
|
||||||
========================= test session starts ==========================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev4
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_sample.py
|
test path 1: test_sample.py
|
||||||
|
|
||||||
test_sample.py F
|
test_sample.py F
|
||||||
|
|
||||||
=============================== FAILURES ===============================
|
================================= FAILURES =================================
|
||||||
_____________________________ test_answer ______________________________
|
_______________________________ test_answer ________________________________
|
||||||
|
|
||||||
def test_answer():
|
def test_answer():
|
||||||
> assert func(3) == 5
|
> assert func(3) == 5
|
||||||
|
@ -50,23 +50,27 @@ Now you can execute the test function::
|
||||||
E + where 4 = func(3)
|
E + where 4 = func(3)
|
||||||
|
|
||||||
test_sample.py:4: AssertionError
|
test_sample.py:4: AssertionError
|
||||||
======================= 1 failed in 0.02 seconds =======================
|
========================= 1 failed in 0.02 seconds =========================
|
||||||
|
|
||||||
We got a failure because our little ``func(3)`` call did not return ``5``.
|
We told py.test to run the ``test_sample.py`` file and it :ref:`discovered` the
|
||||||
A few notes on this little test invocation:
|
``test_answer`` function because of the ``test_`` prefix. We got a
|
||||||
|
failure because our little ``func(3)`` call did not return ``5``.
|
||||||
|
|
||||||
* ``test_answer`` was identified as a test function because of the
|
.. note::
|
||||||
``test_`` prefix,
|
|
||||||
|
|
||||||
* we conveniently used the standard `assert statement`_ and the failure
|
You can simply use the `assert statement`_ for coding expectations because
|
||||||
report shows us the intermediate values.
|
intermediate values will be presented to you. Or to put it bluntly,
|
||||||
|
there is no need to learn all `the JUnit legacy methods`_ for expressing
|
||||||
|
assertions.
|
||||||
|
|
||||||
|
.. _`the JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases
|
||||||
|
|
||||||
.. _`assert statement`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement
|
.. _`assert statement`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement
|
||||||
|
|
||||||
Asserting that a certain exception is raised
|
Asserting a certain exception is raised
|
||||||
--------------------------------------------------------------
|
--------------------------------------------------------------
|
||||||
|
|
||||||
If you want to assert a test raises a certain exception you can
|
If you want to assert some code raises an exception you can
|
||||||
use the ``raises`` helper::
|
use the ``raises`` helper::
|
||||||
|
|
||||||
# content of test_sysexit.py
|
# content of test_sysexit.py
|
||||||
|
@ -78,18 +82,49 @@ use the ``raises`` helper::
|
||||||
with py.test.raises(SystemExit):
|
with py.test.raises(SystemExit):
|
||||||
f()
|
f()
|
||||||
|
|
||||||
Running it with::
|
Running it with, this time in "quiet" reporting mode::
|
||||||
|
|
||||||
$ py.test test_sysexit.py
|
$ py.test -q test_sysexit.py
|
||||||
========================= test session starts ==========================
|
.
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev4
|
1 passed in 0.01 seconds
|
||||||
test path 1: test_sysexit.py
|
|
||||||
|
|
||||||
test_sysexit.py .
|
.. todo:: For further ways to assert exceptions see the :pyfunc:`raises`
|
||||||
|
|
||||||
======================= 1 passed in 0.01 seconds =======================
|
Grouping multiple tests in a class
|
||||||
|
--------------------------------------------------------------
|
||||||
|
|
||||||
.. For further ways to assert exceptions see the :pyfunc:`raises`
|
If you start to have more than a few tests it often makes sense
|
||||||
|
to group tests logically, in classes and modules. Let's put two
|
||||||
|
tests in a class like this::
|
||||||
|
|
||||||
|
# content of test_class.py
|
||||||
|
class TestClass:
|
||||||
|
def test_one(self):
|
||||||
|
x = "this"
|
||||||
|
assert 'h' in x
|
||||||
|
|
||||||
|
def test_two(self):
|
||||||
|
x = "hello"
|
||||||
|
assert hasattr(x, 'check')
|
||||||
|
|
||||||
|
The two tests will be discovered because of the default `automatic test
|
||||||
|
discovery`_. There is no need to subclass anything. If we now run
|
||||||
|
the module we'll see one passed and one failed test::
|
||||||
|
|
||||||
|
$ py.test -q test_class.py
|
||||||
|
.F
|
||||||
|
================================= FAILURES =================================
|
||||||
|
____________________________ TestClass.test_two ____________________________
|
||||||
|
|
||||||
|
self = <test_class.TestClass instance at 0x1732368>
|
||||||
|
|
||||||
|
def test_two(self):
|
||||||
|
x = "hello"
|
||||||
|
> assert hasattr(x, 'check')
|
||||||
|
E assert hasattr('hello', 'check')
|
||||||
|
|
||||||
|
test_class.py:8: AssertionError
|
||||||
|
1 failed, 1 passed in 0.02 seconds
|
||||||
|
|
||||||
where to go from here
|
where to go from here
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
@ -99,6 +134,47 @@ Here are a few suggestions where to go next:
|
||||||
* :ref:`cmdline` for command line invocation examples
|
* :ref:`cmdline` for command line invocation examples
|
||||||
* :ref:`good practises` for virtualenv, test layout, genscript support
|
* :ref:`good practises` for virtualenv, test layout, genscript support
|
||||||
* :ref:`apiref` for documentation and examples on writing Python tests
|
* :ref:`apiref` for documentation and examples on writing Python tests
|
||||||
* :ref:`examples` for more complex examples
|
|
||||||
|
.. _`installation issues`:
|
||||||
|
|
||||||
|
Installation issues
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
easy_install or pip not found?
|
||||||
|
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
Consult distribute_ to install the ``easy_install`` tool on your machine.
|
||||||
|
You may also use the original but somewhat older `setuptools`_ project
|
||||||
|
although we generally recommend to use ``distribute`` because it contains
|
||||||
|
more bug fixes and also works for Python3.
|
||||||
|
|
||||||
|
For Python2 you can also consult pip_ for the popular ``pip`` tool.
|
||||||
|
|
||||||
|
However, If you want to install on Python3 you need to use Distribute_ which
|
||||||
|
provides the ``easy_install`` utility.
|
||||||
|
|
||||||
|
|
||||||
|
py.test not found on Windows despite installation?
|
||||||
|
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
|
||||||
|
.. _`Python for Windows`: http://www.imladris.com/Scripts/PythonForWindows.html
|
||||||
|
|
||||||
|
|
||||||
|
- **Windows**: If "easy_install" or "py.test" are not found
|
||||||
|
please see here for preparing your environment for running
|
||||||
|
command line tools: `Python for Windows`_. You may alternatively
|
||||||
|
use an `ActivePython install`_ which makes command line tools
|
||||||
|
automatically available under Windows.
|
||||||
|
|
||||||
|
.. _`ActivePython install`: http://www.activestate.com/activepython/downloads
|
||||||
|
|
||||||
|
.. _`Jython does not create command line launchers`: http://bugs.jython.org/issue1491
|
||||||
|
|
||||||
|
- **Jython2.5.1 on Windows XP**: `Jython does not create command line launchers`_
|
||||||
|
so ``py.test`` will not work correctly. You may install py.test on
|
||||||
|
CPython and type ``py.test --genscript=mytest`` and then use
|
||||||
|
``jython mytest`` to run py.test for your tests to run in Jython.
|
||||||
|
|
||||||
|
:ref:`examples` for more complex examples
|
||||||
|
|
||||||
.. include:: links.inc
|
.. include:: links.inc
|
||||||
|
|
|
@ -2,51 +2,91 @@
|
||||||
.. highlightlang:: python
|
.. highlightlang:: python
|
||||||
.. _`good practises`:
|
.. _`good practises`:
|
||||||
|
|
||||||
Good Practises
|
Good Integration Practises
|
||||||
=================================================
|
=================================================
|
||||||
|
|
||||||
Recommendation: install tool and dependencies virtually
|
work with virtual environments
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
We recommend to work with virtual environments
|
We recommend to work with virtualenv_ environments and use easy_install_
|
||||||
(e.g. virtualenv_ or buildout_ based) and use easy_install_
|
(or pip_) for installing your application dependencies as well as
|
||||||
(or pip_) for installing py.test/pylib and any dependencies
|
the ``pytest`` package itself. This way you get a much more reproducible
|
||||||
you need to run your tests. Local virtual Python environments
|
environment. A good tool to help you automate test runs against multiple
|
||||||
(as opposed to system-wide "global" environments) make for a more
|
dependency configurations or Python interpreters is `tox`_,
|
||||||
reproducible and reliable test environment.
|
independently created by the main py.test author. The latter
|
||||||
|
is also useful for integration with the continous integration
|
||||||
|
server Hudson_.
|
||||||
|
|
||||||
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
||||||
.. _`buildout`: http://www.buildout.org/
|
.. _`buildout`: http://www.buildout.org/
|
||||||
.. _pip: http://pypi.python.org/pypi/pip
|
.. _pip: http://pypi.python.org/pypi/pip
|
||||||
|
|
||||||
|
|
||||||
|
Choosing a test layout / import rules
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
py.test supports common test layouts:
|
||||||
|
|
||||||
|
* inlining test directories into your application package, useful if you want to
|
||||||
|
keep (unit) tests and actually tested code close together::
|
||||||
|
|
||||||
|
mypkg/
|
||||||
|
__init__.py
|
||||||
|
appmodule.py
|
||||||
|
...
|
||||||
|
test/
|
||||||
|
test_app.py
|
||||||
|
...
|
||||||
|
|
||||||
|
* putting tests into an extra directory outside your actual application
|
||||||
|
code, useful if you have many functional tests or want to keep
|
||||||
|
tests separate from actual application code::
|
||||||
|
|
||||||
|
mypkg/
|
||||||
|
__init__.py
|
||||||
|
appmodule.py
|
||||||
|
tests/
|
||||||
|
test_app.py
|
||||||
|
...
|
||||||
|
|
||||||
|
You can always run your tests by pointing to it::
|
||||||
|
|
||||||
|
py.test tests/test_app.py # for external test dirs
|
||||||
|
py.test mypkg/test/test_app.py # for inlined test dirs
|
||||||
|
py.test mypkg # run tests in all below test directories
|
||||||
|
py.test # run all tests below current dir
|
||||||
|
...
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Test modules are imported under their fully qualified name as follows:
|
||||||
|
|
||||||
|
* ``basedir`` = first upward directory not containing an ``__init__.py``
|
||||||
|
|
||||||
|
* perform ``sys.path.insert(0, basedir)``.
|
||||||
|
|
||||||
|
* ``import path.to.test_module``
|
||||||
|
|
||||||
.. _standalone:
|
.. _standalone:
|
||||||
|
|
||||||
|
|
||||||
Choosing a test layout
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
py.test supports common test layouts.
|
|
||||||
|
|
||||||
XXX
|
|
||||||
|
|
||||||
.. _`genscript method`:
|
.. _`genscript method`:
|
||||||
|
|
||||||
Generating a py.test standalone Script
|
Generating a py.test standalone Script
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
|
|
||||||
If you are a maintainer or application developer and want users
|
If you are a maintainer or application developer and want others
|
||||||
to run tests you can use a facility to generate a standalone
|
to easily run tests you can generate a completely standalone "py.test"
|
||||||
"py.test" script that you can tell users to run::
|
script::
|
||||||
|
|
||||||
py.test --genscript=runtests.py
|
py.test --genscript=runtests.py
|
||||||
|
|
||||||
will generate a ``mytest`` script that is, in fact, a ``py.test`` under
|
generates a ``runtests.py`` script which is a fully functional basic
|
||||||
disguise. You can tell people to download and then e.g. run it like this::
|
``py.test`` script, running unchanged under Python2 and Python3.
|
||||||
|
You can tell people to download and then e.g. run it like this to
|
||||||
|
produce a Paste URL::
|
||||||
|
|
||||||
python runtests.py --pastebin=all
|
python runtests.py --pastebin=all
|
||||||
|
|
||||||
and ask them to send you the resulting URL. The resulting script has
|
and ask them to send you the resulting URL.
|
||||||
all core features and runs unchanged under Python2 and Python3 interpreters.
|
|
||||||
|
|
||||||
.. _`Distribute for installation`: http://pypi.python.org/pypi/distribute#installation-instructions
|
.. _`Distribute for installation`: http://pypi.python.org/pypi/distribute#installation-instructions
|
||||||
.. _`distribute installation`: http://pypi.python.org/pypi/distribute
|
.. _`distribute installation`: http://pypi.python.org/pypi/distribute
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
py.test: no-boilerplate testing with Python
|
py.test: no-boilerplate testing with Python
|
||||||
==============================================
|
==============================================
|
||||||
|
|
||||||
|
.. todolist::
|
||||||
|
|
||||||
|
|
||||||
Welcome to ``py.test`` documentation:
|
Welcome to ``py.test`` documentation:
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
@ -8,7 +11,7 @@ Welcome to ``py.test`` documentation:
|
||||||
|
|
||||||
overview
|
overview
|
||||||
apiref
|
apiref
|
||||||
customize
|
plugins
|
||||||
examples
|
examples
|
||||||
talks
|
talks
|
||||||
develop
|
develop
|
||||||
|
|
|
@ -16,3 +16,4 @@
|
||||||
.. _`pip`: http://pypi.python.org/pypi/pip
|
.. _`pip`: http://pypi.python.org/pypi/pip
|
||||||
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
||||||
.. _hudson: http://hudson-ci.org/
|
.. _hudson: http://hudson-ci.org/
|
||||||
|
.. _tox: http://codespeak.net/tox
|
||||||
|
|
12
doc/mark.txt
12
doc/mark.txt
|
@ -88,8 +88,8 @@ You can use the ``-k`` command line option to select tests::
|
||||||
|
|
||||||
$ py.test -k webtest # running with the above defined examples yields
|
$ py.test -k webtest # running with the above defined examples yields
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: /tmp/doc-exec-11
|
test path 1: /tmp/doc-exec-171
|
||||||
|
|
||||||
test_mark.py ..
|
test_mark.py ..
|
||||||
test_mark_classlevel.py ..
|
test_mark_classlevel.py ..
|
||||||
|
@ -100,8 +100,8 @@ And you can also run all tests except the ones that match the keyword::
|
||||||
|
|
||||||
$ py.test -k-webtest
|
$ py.test -k-webtest
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: /tmp/doc-exec-11
|
test path 1: /tmp/doc-exec-171
|
||||||
|
|
||||||
===================== 4 tests deselected by '-webtest' =====================
|
===================== 4 tests deselected by '-webtest' =====================
|
||||||
======================= 4 deselected in 0.01 seconds =======================
|
======================= 4 deselected in 0.01 seconds =======================
|
||||||
|
@ -110,8 +110,8 @@ Or to only select the class::
|
||||||
|
|
||||||
$ py.test -kTestClass
|
$ py.test -kTestClass
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: /tmp/doc-exec-11
|
test path 1: /tmp/doc-exec-171
|
||||||
|
|
||||||
test_mark_classlevel.py ..
|
test_mark_classlevel.py ..
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,8 @@ will be undone.
|
||||||
.. background check:
|
.. background check:
|
||||||
$ py.test
|
$ py.test
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: /tmp/doc-exec-296
|
test path 1: /tmp/doc-exec-172
|
||||||
|
|
||||||
============================= in 0.00 seconds =============================
|
============================= in 0.00 seconds =============================
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,282 @@
|
||||||
|
Writing, managing and understanding plugins
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
.. _`local plugin`:
|
||||||
|
|
||||||
|
py.test implements all aspects of configuration, collection, running and reporting by calling `well specified hooks`_. Virtually any Python module can be registered as a plugin. It can implement any number of hook functions (usually two or three) which all have a ``pytest_`` prefix, making hook functions easy to distinguish and find. There are three basic locations types::
|
||||||
|
|
||||||
|
* builtin plugins: loaded from py.test's own `pytest/plugin`_ directory.
|
||||||
|
* `external plugins`_: modules discovered through `setuptools entry points`_
|
||||||
|
* `conftest.py plugins`_: modules auto-discovered in test directories
|
||||||
|
|
||||||
|
.. _`pytest/plugin`: http://bitbucket.org/hpk42/pytest/src/tip/pytest/plugin/
|
||||||
|
.. _`conftest.py plugins`:
|
||||||
|
|
||||||
|
conftest.py: local per-directory plugins
|
||||||
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
local ``conftest.py`` plugins contain directory-specific hook
|
||||||
|
implementations. Collection and test running activities will
|
||||||
|
invoke all hooks defined in "higher up" ``conftest.py`` files.
|
||||||
|
Example: Assume the following layout and content of files::
|
||||||
|
|
||||||
|
a/conftest.py:
|
||||||
|
def pytest_runtest_setup(item):
|
||||||
|
# called for running each test in 'a' directory
|
||||||
|
print ("setting up", item)
|
||||||
|
|
||||||
|
a/test_in_subdir.py:
|
||||||
|
def test_sub():
|
||||||
|
pass
|
||||||
|
|
||||||
|
test_flat.py:
|
||||||
|
def test_flat():
|
||||||
|
pass
|
||||||
|
|
||||||
|
Here is how you might run it::
|
||||||
|
|
||||||
|
py.test test_flat.py # will not show "setting up"
|
||||||
|
py.test a/test_sub.py # will show "setting up"
|
||||||
|
|
||||||
|
A note on ordering: ``py.test`` loads all ``conftest.py`` files upwards
|
||||||
|
from the command line file arguments. It usually performs look up
|
||||||
|
right-to-left, i.e. the hooks in "closer" conftest files will be called
|
||||||
|
earlier than further away ones.
|
||||||
|
|
||||||
|
.. Note::
|
||||||
|
If you have ``conftest.py`` files which do not reside in a
|
||||||
|
python package directory (i.e. one containing an ``__init__.py``) then
|
||||||
|
"import conftest" can be ambigous because there might be other
|
||||||
|
``conftest.py`` files as well on your PYTHONPATH or ``sys.path``.
|
||||||
|
It is thus good practise for projects to either put ``conftest.py``
|
||||||
|
under a package scope or to never import anything from a
|
||||||
|
conftest.py file.
|
||||||
|
|
||||||
|
.. _`installing plugins`:
|
||||||
|
.. _`external plugins`:
|
||||||
|
|
||||||
|
Installing External Plugins
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
|
Installing a plugin happens through any usual Python installation
|
||||||
|
tool, for example::
|
||||||
|
|
||||||
|
pip install pytest-NAME
|
||||||
|
pip uninstall pytest-NAME
|
||||||
|
|
||||||
|
If a plugin is installed, py.test automatically finds and integrates it,
|
||||||
|
there is no need to activate it. If you don't need a plugin anymore simply
|
||||||
|
de-install it. You can find a list of valid plugins through a
|
||||||
|
`pytest- pypi.python.org search`_.
|
||||||
|
|
||||||
|
.. _`available installable plugins`:
|
||||||
|
.. _`pytest- pypi.python.org search`: http://pypi.python.org/pypi?%3Aaction=search&term=pytest-&submit=search
|
||||||
|
|
||||||
|
.. _`setuptools entry points`:
|
||||||
|
|
||||||
|
Writing an installable plugin
|
||||||
|
------------------------------------------------------
|
||||||
|
|
||||||
|
.. _`Distribute`: http://pypi.python.org/pypi/distribute
|
||||||
|
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
|
||||||
|
|
||||||
|
If you want to write a plugin, there are many real-life examples
|
||||||
|
you can copy from:
|
||||||
|
|
||||||
|
* around 20 `builtin plugins`_ which comprise py.test's own functionality
|
||||||
|
* around 10 `external plugins`_ providing additional features
|
||||||
|
|
||||||
|
If you want to make your plugin externally available, you
|
||||||
|
may define a so called entry point for your distribution so
|
||||||
|
that ``py.test`` finds your plugin module. Entry points are
|
||||||
|
a feature that is provided by `setuptools`_ or `Distribute`_.
|
||||||
|
The concrete entry point is ``pytest11``. To make your plugin
|
||||||
|
available you can insert the following lines in your
|
||||||
|
setuptools/distribute-based setup-invocation:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
# sample ./setup.py file
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="myproject",
|
||||||
|
packages = ['myproject']
|
||||||
|
|
||||||
|
# the following makes a plugin available to py.test
|
||||||
|
entry_points = {
|
||||||
|
'pytest11': [
|
||||||
|
'name_of_plugin = myproject.pluginmodule',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
If a package is installed this way, py.test will load
|
||||||
|
``myproject.pluginmodule`` and accordingly call functions
|
||||||
|
if they match the `well specified hooks`_.
|
||||||
|
|
||||||
|
Plugin discovery order at tool startup
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
py.test loads plugin modules at tool startup in the following way:
|
||||||
|
|
||||||
|
* by loading all builtin plugins
|
||||||
|
|
||||||
|
* by loading all plugins registered through `setuptools entry points`_.
|
||||||
|
|
||||||
|
* by pre-scanning the command line for the ``-p name`` option
|
||||||
|
and loading the specified plugin before actual command line parsing.
|
||||||
|
|
||||||
|
* by loading all :file:`conftest.py` files as inferred by the command line
|
||||||
|
invocation (test files and all of its *parent* directories).
|
||||||
|
Note that ``conftest.py`` files from *sub* directories are by default
|
||||||
|
not loaded at tool startup.
|
||||||
|
|
||||||
|
* by recursively loading all plugins specified by the
|
||||||
|
``pytest_plugins`` variable in ``conftest.py`` files
|
||||||
|
|
||||||
|
Requiring/Loading plugins in a test module or conftest file
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
You can require plugins in a test module or a conftest file like this::
|
||||||
|
|
||||||
|
pytest_plugins = "name1", "name2",
|
||||||
|
|
||||||
|
When the test module or conftest plugin is loaded the specified plugins
|
||||||
|
will be loaded as well. You can also use dotted path like this::
|
||||||
|
|
||||||
|
pytest_plugins = "myapp.testsupport.myplugin"
|
||||||
|
|
||||||
|
which will import the specified module as a py.test plugin.
|
||||||
|
|
||||||
|
.. _`setuptools entry points`:
|
||||||
|
.. _registered:
|
||||||
|
|
||||||
|
|
||||||
|
Accessing another plugin by name
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
If a plugin wants to collaborate with code from
|
||||||
|
another plugin it can obtain a reference through
|
||||||
|
the plugin manager like this:
|
||||||
|
|
||||||
|
.. sourcecode:: python
|
||||||
|
|
||||||
|
plugin = config.pluginmanager.getplugin("name_of_plugin")
|
||||||
|
|
||||||
|
If you want to look at the names of existing plugins, use
|
||||||
|
the ``--traceconfig`` option.
|
||||||
|
|
||||||
|
.. _`well specified hooks`:
|
||||||
|
|
||||||
|
py.test hook reference
|
||||||
|
====================================
|
||||||
|
|
||||||
|
hook specification and validation
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
py.test calls hook functions to implement initialization, running,
|
||||||
|
test execution and reporting. When py.test loads a plugin it validates
|
||||||
|
that all hook functions conform to their respective hook specification.
|
||||||
|
Each hook function name and its argument names need to match a hook
|
||||||
|
specification exactly but it is allowed for a hook function to accept
|
||||||
|
*less* parameters than specified. If you mistype argument names or the
|
||||||
|
hook name itself you get useful errors.
|
||||||
|
|
||||||
|
initialisation, command line and configuration hooks
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
.. currentmodule:: pytest.hookspec
|
||||||
|
|
||||||
|
.. autofunction:: pytest_cmdline_parse
|
||||||
|
.. autofunction:: pytest_namespace
|
||||||
|
.. autofunction:: pytest_addoption
|
||||||
|
.. autofunction:: pytest_cmdline_main
|
||||||
|
.. autofunction:: pytest_configure
|
||||||
|
.. autofunction:: pytest_unconfigure
|
||||||
|
|
||||||
|
generic "runtest" hooks
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
All all runtest related hooks receive a :py:class:`pytest.collect.Item` object.
|
||||||
|
|
||||||
|
.. autofunction:: pytest_runtest_protocol
|
||||||
|
.. autofunction:: pytest_runtest_setup
|
||||||
|
.. autofunction:: pytest_runtest_call
|
||||||
|
.. autofunction:: pytest_runtest_teardown
|
||||||
|
.. autofunction:: pytest_runtest_makereport
|
||||||
|
|
||||||
|
For deeper understanding you may look at the default implementation of
|
||||||
|
these hooks in :py:mod:`pytest.plugin.runner` and maybe also
|
||||||
|
in :py:mod:`pytest.plugin.pdb` which intercepts creation
|
||||||
|
of reports in order to drop to interactive debugging.
|
||||||
|
|
||||||
|
The :py:mod:`pytest.plugin.terminal` reported specifically uses
|
||||||
|
the reporting hook to print information about a test run.
|
||||||
|
|
||||||
|
collection hooks
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
py.test calls the following hooks for collecting files and directories:
|
||||||
|
|
||||||
|
.. autofunction:: pytest_ignore_collect
|
||||||
|
.. autofunction:: pytest_collect_directory
|
||||||
|
.. autofunction:: pytest_collect_file
|
||||||
|
|
||||||
|
For influencing the collection of objects in Python modules
|
||||||
|
you can use the following hook:
|
||||||
|
|
||||||
|
.. autofunction:: pytest_pycollect_makeitem
|
||||||
|
|
||||||
|
|
||||||
|
reporting hooks
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Collection related reporting hooks:
|
||||||
|
|
||||||
|
.. autofunction: pytest_collectstart
|
||||||
|
.. autofunction: pytest_itemcollected
|
||||||
|
.. autofunction: pytest_collectreport
|
||||||
|
.. autofunction: pytest_deselected
|
||||||
|
|
||||||
|
And here is the central hook for reporting about
|
||||||
|
test execution:
|
||||||
|
|
||||||
|
.. autofunction: pytest_runtest_logreport
|
||||||
|
|
||||||
|
|
||||||
|
Reference of important objects involved in hooks
|
||||||
|
===========================================================
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.config.Config
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.config.Parser
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.session.Node(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
..
|
||||||
|
.. autoclass:: pytest.plugin.session.File(fspath, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.session.Item(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.python.Module(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.python.Class(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.python.Function(name, parent)
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.runner.CallInfo
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.plugin.runner.TestReport
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ Running this would result in a passed test except for the last
|
||||||
|
|
||||||
$ py.test test_tmpdir.py
|
$ py.test test_tmpdir.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_tmpdir.py
|
test path 1: test_tmpdir.py
|
||||||
|
|
||||||
test_tmpdir.py F
|
test_tmpdir.py F
|
||||||
|
@ -34,7 +34,7 @@ Running this would result in a passed test except for the last
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
_____________________________ test_create_file _____________________________
|
_____________________________ test_create_file _____________________________
|
||||||
|
|
||||||
tmpdir = local('/tmp/pytest-427/test_create_file0')
|
tmpdir = local('/tmp/pytest-1248/test_create_file0')
|
||||||
|
|
||||||
def test_create_file(tmpdir):
|
def test_create_file(tmpdir):
|
||||||
p = tmpdir.mkdir("sub").join("hello.txt")
|
p = tmpdir.mkdir("sub").join("hello.txt")
|
||||||
|
@ -45,7 +45,7 @@ Running this would result in a passed test except for the last
|
||||||
E assert 0
|
E assert 0
|
||||||
|
|
||||||
test_tmpdir.py:7: AssertionError
|
test_tmpdir.py:7: AssertionError
|
||||||
========================= 1 failed in 0.03 seconds =========================
|
========================= 1 failed in 0.04 seconds =========================
|
||||||
|
|
||||||
.. _`base temporary directory`:
|
.. _`base temporary directory`:
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
unittest.py style testing integration
|
unittest.TestCase support
|
||||||
=====================================================================
|
=====================================================================
|
||||||
|
|
||||||
py.test has limited support for running Python `unittest.py style`_ tests.
|
py.test has limited support for running Python `unittest.py style`_ tests.
|
||||||
It will automatically collect ``unittest.TestCase`` subclasses
|
It will automatically collect ``unittest.TestCase`` subclasses
|
||||||
and their ``test`` methods in test files. It will invoke
|
and their ``test`` methods in test files. It will invoke
|
||||||
``setUp/tearDown`` methods but also perform py.test's standard ways
|
``setUp/tearDown`` methods but also perform py.test's standard ways
|
||||||
of treating tests like IO capturing::
|
of treating tests like e.g. IO capturing::
|
||||||
|
|
||||||
# content of test_unittest.py
|
# content of test_unittest.py
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ Running it yields::
|
||||||
|
|
||||||
$ py.test test_unittest.py
|
$ py.test test_unittest.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
|
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
|
||||||
test path 1: test_unittest.py
|
test path 1: test_unittest.py
|
||||||
|
|
||||||
test_unittest.py F
|
test_unittest.py F
|
||||||
|
@ -55,7 +55,5 @@ Running it yields::
|
||||||
hello
|
hello
|
||||||
========================= 1 failed in 0.02 seconds =========================
|
========================= 1 failed in 0.02 seconds =========================
|
||||||
|
|
||||||
This plugin is enabled by default.
|
|
||||||
|
|
||||||
.. _`unittest.py style`: http://docs.python.org/library/unittest.html
|
.. _`unittest.py style`: http://docs.python.org/library/unittest.html
|
||||||
|
|
||||||
|
|
|
@ -54,13 +54,13 @@ Similarly, the following methods are called around each method invocation::
|
||||||
|
|
||||||
def setup_method(self, method):
|
def setup_method(self, method):
|
||||||
""" setup up any state tied to the execution of the given
|
""" setup up any state tied to the execution of the given
|
||||||
method in a class. setup_method is invoked for every
|
method in a class. setup_method is invoked for every
|
||||||
test method of a class.
|
test method of a class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def teardown_method(self, method):
|
def teardown_method(self, method):
|
||||||
""" teardown any state that was previously setup
|
""" teardown any state that was previously setup
|
||||||
with a setup_method call.
|
with a setup_method call.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
If you rather define test functions directly at module level
|
If you rather define test functions directly at module level
|
||||||
|
@ -68,12 +68,13 @@ you can also use the following functions to implement fixtures::
|
||||||
|
|
||||||
def setup_function(function):
|
def setup_function(function):
|
||||||
""" setup up any state tied to the execution of the given
|
""" setup up any state tied to the execution of the given
|
||||||
function. Invoked for every test function in the module.
|
function. Invoked for every test function in the module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def teardown_method(function):
|
def teardown_method(function):
|
||||||
""" teardown any state that was previously setup
|
""" teardown any state that was previously setup
|
||||||
with a setup_function call.
|
with a setup_function call.
|
||||||
|
"""
|
||||||
|
|
||||||
Note that it possible that setup/teardown pairs are invoked multiple
|
Note that it possible that setup/teardown pairs are invoked multiple
|
||||||
times per testing process.
|
times per testing process.
|
||||||
|
|
|
@ -247,7 +247,8 @@ class Config(object):
|
||||||
basetemp = None
|
basetemp = None
|
||||||
|
|
||||||
def __init__(self, pluginmanager=None):
|
def __init__(self, pluginmanager=None):
|
||||||
#: command line option values
|
#: command line option values, usually added via parser.addoption(...)
|
||||||
|
#: or parser.getgroup(...).addoption(...) calls
|
||||||
self.option = CmdOptions()
|
self.option = CmdOptions()
|
||||||
self._parser = Parser(
|
self._parser = Parser(
|
||||||
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
|
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
|
||||||
|
@ -404,7 +405,7 @@ class Config(object):
|
||||||
return self._getconftest(name, path, check=False)
|
return self._getconftest(name, path, check=False)
|
||||||
|
|
||||||
def getvalueorskip(self, name, path=None):
|
def getvalueorskip(self, name, path=None):
|
||||||
""" return getvalue(name) or call py.test.skip if no value exists. """
|
""" (deprecated) return getvalue(name) or call py.test.skip if no value exists. """
|
||||||
try:
|
try:
|
||||||
val = self.getvalue(name, path)
|
val = self.getvalue(name, path)
|
||||||
if val is None:
|
if val is None:
|
||||||
|
|
Loading…
Reference in New Issue