massive documentation refinements

This commit is contained in:
holger krekel 2010-11-02 00:53:53 +01:00
parent 7d495cc250
commit c18cca9d54
27 changed files with 622 additions and 508 deletions

View File

@ -46,7 +46,7 @@ except ImportError:
args = [quote(arg) for arg in args]
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/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"

View File

@ -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
regen:
COLUMNS=76 regendoc --update *.txt */*.txt
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"

View File

@ -7,6 +7,7 @@ py.test reference documentation
.. toctree::
:maxdepth: 2
customize.txt
assert.txt
funcargs.txt
xunit_setup.txt
@ -15,7 +16,7 @@ py.test reference documentation
tmpdir.txt
skipping.txt
mark.txt
recwarn.txt
doctest.txt
recwarn.txt
unittest.txt
doctest.txt

View File

@ -1,5 +1,5 @@
Writing easy assertions in tests
Writing and reporting of assertions in tests
============================================
assert with the ``assert`` statement
@ -21,7 +21,7 @@ assertion fails you will see the value of ``x``::
$ py.test test_assert1.py
=========================== 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_assert1.py F
@ -101,7 +101,7 @@ if you run this module::
$ py.test test_assert2.py
=========================== 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_assert2.py F

View File

@ -11,9 +11,7 @@ Getting help on version, option names, environment vars
py.test --version # shows where pytest was imported from
py.test --funcargs # show available builtin function arguments
py.test --help-config # show configuration values
py.test -h | --help # show help
py.test -h | --help # show help on command line and config file options
Stopping after the first (or N) failures

View File

@ -25,7 +25,8 @@ import sys, os
# Add any Sphinx extension module names here, as strings. They can be extensions
# 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.
templates_path = ['_templates']

View File

@ -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.
- `tetamap`_: Holger Krekel's blog, often about testing and py.test related news.
- `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.
- `commit mailing list`_
- `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
@ -28,20 +30,8 @@ Contact and Communication points
.. _`@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
.. _FOAF: http://en.wikipedia.org/wiki/FOAF
.. _us: http://codespeak.net/mailman/listinfo/py-dev
.. _codespeak: http://codespeak.net/
.. _`py-dev`:
.. _`development mailing list`:
.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev

View File

@ -1,7 +1,3 @@
================================================
Customizing and Extending py.test
================================================
basic test configuration
===================================
@ -58,309 +54,4 @@ builtin configuration file options
py.test --maxfail=2 -rf test_hello.py
.. _`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:

45
doc/discovery.txt Normal file
View File

@ -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.

View File

@ -44,11 +44,7 @@ then you can just invoke ``py.test`` without command line options::
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
test path 1: /tmp/doc-exec-288
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
test path 1: /tmp/doc-exec-197
conftest.py .
example.rst .
mymodule.py .
========================= 3 passed in 0.01 seconds =========================
============================= in 0.00 seconds =============================

View File

@ -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'
=========================== 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_module.py .s
========================= 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 ====================
@ -49,7 +49,7 @@ Or run it including the ``slow`` marked test::
$ py.test test_module.py --runslow
=========================== 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_module.py ..

View File

@ -49,7 +49,7 @@ You can now run the test::
$ py.test test_sample.py
=========================== 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_sample.py F
@ -57,7 +57,7 @@ You can now run the test::
================================= FAILURES =================================
_______________________________ test_answer ________________________________
mysetup = <conftest.MySetup instance at 0x1a09f38>
mysetup = <conftest.MySetup instance at 0x2809a70>
def test_answer(mysetup):
app = mysetup.myapp()
@ -122,12 +122,12 @@ Running it yields::
$ py.test test_ssh.py -rs
=========================== 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_ssh.py s
========================= 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 =========================

View File

@ -25,8 +25,8 @@ now execute the test specification::
nonpython $ py.test test_simple.yml
=========================== test session starts ============================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev10
test path 1: /home/hpk/p/pytest/doc/example/nonpython
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
test path 1: test_simple.yml
test_simple.yml .F
@ -35,7 +35,7 @@ now execute the test specification::
usecase execution failed
spec failed: 'some': 'other'
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.
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
=========================== 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_simple.yml:1: usecase: ok PASSED
@ -56,7 +56,7 @@ reporting in ``verbose`` mode::
usecase execution failed
spec failed: 'some': 'other'
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
interesting to just look at the collection tree::

View File

@ -1,87 +1,56 @@
Frequent Issues and Questions
Some Issues and Questions
==================================
.. _`installation issues`:
.. note::
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.
If you don't find an answer here, checkout the :ref:`contact channels`
to get help.
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
because of TAB-completion under Bash/Shells. If you hit
``py.<TAB>`` you'll get a list of available development
tools that all share the ``py.`` prefix. Another motivation
was to unify the package ("py.test") and tool filename.
Some historic, some practical reasons: ``py.test`` used to be part of
the ``py`` package which provided several developer utitilities,
all starting with ``py.<TAB>``, providing nice TAB-completion. If
you install ``pip install pycmd`` you get these tools from a separate
package. These days the command line tool could be ``pytest``
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``?
+++++++++++++++++++++++++++++++++++++++++++++++++
py.test and nose_ share basic philosophy when it comes
to running Python tests. In fact,
with py.test-1.1.0 it is ever easier to run many test suites
that currently work with ``nosetests``. nose_ was created
to running Python tests. In fact, you can run many tests
written for unittest or nose with py.test. nose_ was originally created
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
and py.test-1.1 have no counterpart in nose_.
cycle.
.. _features: test/features.html
.. _apipkg: http://pypi.python.org/pypi/apipkg
What's this "magic" with py.test?
++++++++++++++++++++++++++++++++++++++++++
Around 2007 it was claimed that py.test was magic implementation
wise XXX. It has been refactored.
Around 2007 (version ``0.8``) some several people claimed that py.test
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
to show intermediate values if a test fails. If your expression
has side effects the intermediate values may not be the same, obfuscating
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.
It's also true that the no-boilerplate testing is implemented by making
use of the Python assert statement through "re-interpretation":
When an ``assert`` statement fails, py.test re-interprets the expression
to show intermediate values if a test fails. If your expression
has side effects the intermediate values may not be the same, obfuscating
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/__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
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
unittest-style test setup using `xUnit style setup`_
feels natural. For larger test suites, parametrized testing

View File

@ -4,9 +4,11 @@ py.test Features
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
- use the ``assert`` statement for your assertions
- rely on powerful traceback and assertion reporting
- use ``print`` or ``pdb`` debugging on failures

View File

@ -32,7 +32,7 @@ Running the test looks like this::
$ py.test test_simplefactory.py
=========================== 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_simplefactory.py F
@ -133,7 +133,7 @@ Running this::
$ py.test test_example.py
=========================== 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_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::
$ py.test --collectonly test_example.py
<Directory 'doc-exec-341'>
<Directory 'doc-exec-167'>
<Module 'test_example.py'>
<Function 'test_func[0]'>
<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]
=========================== 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_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[8] PASSED
test_example.py:6: test_func[9] FAILED
======================== 9 tests deselected by '7' =========================
================== 1 passed, 9 deselected in 0.01 seconds ==================
================================= FAILURES =================================
_______________________________ 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`:

View File

@ -1,4 +1,4 @@
Getting Started
Installation and Getting Started
===================================
.. _`easy_install`:
@ -20,7 +20,7 @@ To check your installation has installed the correct version::
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
@ -32,17 +32,17 @@ computes a certain value::
def test_answer():
assert func(3) == 5
Now you can execute the test function::
You can execute the test function::
$ py.test test_sample.py
========================= test session starts ==========================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev4
=========================== test session starts ============================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
test path 1: test_sample.py
test_sample.py F
=============================== FAILURES ===============================
_____________________________ test_answer ______________________________
================================= FAILURES =================================
_______________________________ test_answer ________________________________
def test_answer():
> assert func(3) == 5
@ -50,23 +50,27 @@ Now you can execute the test function::
E + where 4 = func(3)
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``.
A few notes on this little test invocation:
We told py.test to run the ``test_sample.py`` file and it :ref:`discovered` the
``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
``test_`` prefix,
.. note::
* we conveniently used the standard `assert statement`_ and the failure
report shows us the intermediate values.
You can simply use the `assert statement`_ for coding expectations because
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
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::
# content of test_sysexit.py
@ -78,18 +82,49 @@ use the ``raises`` helper::
with py.test.raises(SystemExit):
f()
Running it with::
Running it with, this time in "quiet" reporting mode::
$ py.test test_sysexit.py
========================= test session starts ==========================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev4
test path 1: test_sysexit.py
test_sysexit.py .
======================= 1 passed in 0.01 seconds =======================
$ py.test -q test_sysexit.py
.
1 passed in 0.01 seconds
.. For further ways to assert exceptions see the :pyfunc:`raises`
.. todo:: For further ways to assert exceptions see the :pyfunc:`raises`
Grouping multiple tests in a class
--------------------------------------------------------------
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
-------------------------------------
@ -99,6 +134,47 @@ Here are a few suggestions where to go next:
* :ref:`cmdline` for command line invocation examples
* :ref:`good practises` for virtualenv, test layout, genscript support
* :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

View File

@ -2,51 +2,91 @@
.. highlightlang:: python
.. _`good practises`:
Good Practises
Good Integration Practises
=================================================
Recommendation: install tool and dependencies virtually
work with virtual environments
-----------------------------------------------------------
We recommend to work with virtual environments
(e.g. virtualenv_ or buildout_ based) and use easy_install_
(or pip_) for installing py.test/pylib and any dependencies
you need to run your tests. Local virtual Python environments
(as opposed to system-wide "global" environments) make for a more
reproducible and reliable test environment.
We recommend to work with virtualenv_ environments and use easy_install_
(or pip_) for installing your application dependencies as well as
the ``pytest`` package itself. This way you get a much more reproducible
environment. A good tool to help you automate test runs against multiple
dependency configurations or Python interpreters is `tox`_,
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
.. _`buildout`: http://www.buildout.org/
.. _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:
Choosing a test layout
----------------------------
py.test supports common test layouts.
XXX
.. _`genscript method`:
Generating a py.test standalone Script
-------------------------------------------
If you are a maintainer or application developer and want users
to run tests you can use a facility to generate a standalone
"py.test" script that you can tell users to run::
If you are a maintainer or application developer and want others
to easily run tests you can generate a completely standalone "py.test"
script::
py.test --genscript=runtests.py
will generate a ``mytest`` script that is, in fact, a ``py.test`` under
disguise. You can tell people to download and then e.g. run it like this::
generates a ``runtests.py`` script which is a fully functional basic
``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
and ask them to send you the resulting URL. The resulting script has
all core features and runs unchanged under Python2 and Python3 interpreters.
and ask them to send you the resulting URL.
.. _`Distribute for installation`: http://pypi.python.org/pypi/distribute#installation-instructions
.. _`distribute installation`: http://pypi.python.org/pypi/distribute

View File

@ -1,5 +1,8 @@
py.test: no-boilerplate testing with Python
==============================================
.. todolist::
Welcome to ``py.test`` documentation:
@ -8,7 +11,7 @@ Welcome to ``py.test`` documentation:
overview
apiref
customize
plugins
examples
talks
develop

View File

@ -16,3 +16,4 @@
.. _`pip`: http://pypi.python.org/pypi/pip
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
.. _hudson: http://hudson-ci.org/
.. _tox: http://codespeak.net/tox

View File

@ -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
=========================== test session starts ============================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
test path 1: /tmp/doc-exec-11
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
test path 1: /tmp/doc-exec-171
test_mark.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
=========================== test session starts ============================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
test path 1: /tmp/doc-exec-11
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
test path 1: /tmp/doc-exec-171
===================== 4 tests deselected by '-webtest' =====================
======================= 4 deselected in 0.01 seconds =======================
@ -110,8 +110,8 @@ Or to only select the class::
$ py.test -kTestClass
=========================== test session starts ============================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
test path 1: /tmp/doc-exec-11
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
test path 1: /tmp/doc-exec-171
test_mark_classlevel.py ..

View File

@ -39,8 +39,8 @@ will be undone.
.. background check:
$ py.test
=========================== test session starts ============================
platform linux2 -- Python 2.6.5 -- pytest-2.0.0dev0
test path 1: /tmp/doc-exec-296
platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev17
test path 1: /tmp/doc-exec-172
============================= in 0.00 seconds =============================

282
doc/plugins.txt Normal file
View File

@ -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:

View File

@ -26,7 +26,7 @@ Running this would result in a passed test except for the last
$ py.test test_tmpdir.py
=========================== 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_tmpdir.py F
@ -34,7 +34,7 @@ Running this would result in a passed test except for the last
================================= FAILURES =================================
_____________________________ test_create_file _____________________________
tmpdir = local('/tmp/pytest-427/test_create_file0')
tmpdir = local('/tmp/pytest-1248/test_create_file0')
def test_create_file(tmpdir):
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
test_tmpdir.py:7: AssertionError
========================= 1 failed in 0.03 seconds =========================
========================= 1 failed in 0.04 seconds =========================
.. _`base temporary directory`:

View File

@ -1,11 +1,11 @@
unittest.py style testing integration
unittest.TestCase support
=====================================================================
py.test has limited support for running Python `unittest.py style`_ tests.
It will automatically collect ``unittest.TestCase`` subclasses
and their ``test`` methods in test files. It will invoke
``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
@ -21,7 +21,7 @@ Running it yields::
$ py.test test_unittest.py
=========================== 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_unittest.py F
@ -55,7 +55,5 @@ Running it yields::
hello
========================= 1 failed in 0.02 seconds =========================
This plugin is enabled by default.
.. _`unittest.py style`: http://docs.python.org/library/unittest.html

View File

@ -54,13 +54,13 @@ Similarly, the following methods are called around each method invocation::
def setup_method(self, method):
""" setup up any state tied to the execution of the given
method in a class. setup_method is invoked for every
test method of a class.
method in a class. setup_method is invoked for every
test method of a class.
"""
def teardown_method(self, method):
""" 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
@ -68,12 +68,13 @@ you can also use the following functions to implement fixtures::
def setup_function(function):
""" 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):
""" 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
times per testing process.

View File

@ -247,7 +247,8 @@ class Config(object):
basetemp = 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._parser = Parser(
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
@ -404,7 +405,7 @@ class Config(object):
return self._getconftest(name, path, check=False)
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:
val = self.getvalue(name, path)
if val is None: