refine and document conftest loading and handling.

--HG--
branch : trunk
This commit is contained in:
holger krekel 2010-10-13 18:45:07 +02:00
parent f3fb91e296
commit 9925ac883e
3 changed files with 109 additions and 66 deletions

View File

@ -15,38 +15,6 @@ You can see command line options by running::
This will display all available command line options This will display all available command line options
in your specific environment. in your specific environment.
.. _`project-specific test configuration`:
.. _`collect_ignore`:
conftest.py: project specific hooks and configuration
--------------------------------------------------------
A unique feature of py.test are its ``conftest.py`` files which
allow to:
* `set option defaults`_
or set particular variables to influence the testing process:
* ``pytest_plugins``: list of named plugins to load
* ``collect_ignore``: list of paths to ignore during test collection, relative to the containing ``conftest.py`` file
* ``rsyncdirs``: list of to-be-rsynced directories for distributed
testing, relative to the containing ``conftest.py`` file.
You may put a conftest.py files in your project root directory or into
your package directory if you want to add project-specific test options.
``py.test`` loads all ``conftest.py`` files upwards from the command
line file arguments. It usually looks up configuration values
right-to-left, i.e. the closer conftest files will be checked first.
This means you can have a ``conftest.py`` in your very home directory to
have some global configuration values.
.. _`specify funcargs`: funcargs.html#application-setup-tutorial-example
.. _`set option defaults`:
setting persistent option defaults setting persistent option defaults
------------------------------------ ------------------------------------
@ -57,13 +25,13 @@ py.test will lookup option values in this order:
* conftest.py files * conftest.py files
* environment variables * environment variables
To find out about the particular switches and type:: To get an overview on existing names and settings type::
py.test --help-config py.test --help-config
This will print information about all options in your This will print information about all available options
environment, including your local plugins. in your environment, including your local plugins and
command line options.
.. _`function arguments`: funcargs.html .. _`function arguments`: funcargs.html
.. _`extensions`: .. _`extensions`:
@ -73,26 +41,51 @@ Plugin basics and project configuration
.. _`local plugin`: .. _`local plugin`:
project specific "local" or named "global" plugins 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
-------------------------------------------------------------- --------------------------------------------------------------
py.test implements much of its functionality by calling `well specified local `conftest.py` plugins are usually automatically loaded and
hooks`_. Python modules which contain such hook functions are called registered but its contained hooks are only called when collecting or
plugins. Hook functions are discovered in ``conftest.py`` files or in running tests in files or directories next to or below the ``conftest.py``
`named plugins`_. ``conftest.py`` files are sometimes called file. Assume the following layout and content of files::
"anonymous" or conftest plugins. They are useful for keeping test
extensions close to your application. Named plugins are normal python a/conftest.py:
modules or packages that can be distributed separately. Named plugins def pytest_runtest_setup(item):
need to follow a naming pattern; they have an all lowercase ``pytest_`` print ("setting up", item)
prefixed name. While conftest plugins are discovered automatically,
named plugins must be explicitely specified. 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 looks up configuration values or hooks
right-to-left, i.e. the closer conftest files are checked before
the further away ones. This means you can have a ``conftest.py``
in your home directory to provide global configuration values.
.. Note::
if you have ``conftest.py`` files which do not reside in a
python package directory (one containing an ``__init__.py``) then
"import conftest" will be ambigous and should be avoided. **If you
want to import anything from a ``conftest.py`` file it is better
to put it inside a package to disambiguate.
.. _`named plugins`: plugin/index.html .. _`named plugins`: plugin/index.html
.. _`tool startup`:
.. _`loaded at tool startup`:
.. _`test tool starts up`:
Plugin discovery at tool startup Plugin discovery at tool startup
-------------------------------------------- --------------------------------------------
@ -106,10 +99,10 @@ py.test loads plugin modules at tool startup in the following way:
* by pre-scanning the command line for the ``-p name`` option * by pre-scanning the command line for the ``-p name`` option
and loading the specified plugin before actual command line parsing. and loading the specified plugin before actual command line parsing.
* by loading all `conftest.py plugin`_ files as inferred by the command line * by loading all :file:`conftest.py` files as inferred by the command line
invocation (test files and all of its parent directories). invocation (test files and all of its *parent* directories).
Note that ``conftest.py`` files from sub directories are loaded Note that ``conftest.py`` files from *sub* directories are by default
during test collection and not at tool startup. not loaded at tool startup.
* by recursively loading all plugins specified by the * by recursively loading all plugins specified by the
``pytest_plugins`` variable in a ``conftest.py`` file ``pytest_plugins`` variable in a ``conftest.py`` file
@ -132,8 +125,8 @@ must be lowercase.
Writing per-project plugins (conftest.py) Writing per-project plugins (conftest.py)
------------------------------------------------------ ------------------------------------------------------
The purpose of ``conftest.py`` files is to allow `project-specific The purpose of :file:`conftest.py` files is to allow project-specific
test configuration`_. They thus make for a good place to implement test customization. They thus make for a good place to implement
project-specific test related features through hooks. For example you may project-specific test related features through hooks. For example you may
set the `collect_ignore`_ variable depending on a command line option set the `collect_ignore`_ variable depending on a command line option
by defining the following hook in a ``conftest.py`` file:: by defining the following hook in a ``conftest.py`` file::
@ -147,6 +140,7 @@ by defining the following hook in a ``conftest.py`` file::
collect_ignore[:] = [] collect_ignore[:] = []
.. _`setuptools entry points`: .. _`setuptools entry points`:
.. _registered:
Writing setuptools-registered plugins Writing setuptools-registered plugins
------------------------------------------------------ ------------------------------------------------------
@ -350,3 +344,30 @@ Reference of important objects involved in hooks
.. autoclass:: pytest.plugin.runner.TestReport .. autoclass:: pytest.plugin.runner.TestReport
:members: :members:
conftest.py configuration files
=================================================
conftest.py reference docs
A unique feature of py.test are its ``conftest.py`` files which allow
project and directory specific customizations to testing.
* `set option defaults`_
or set particular variables to influence the testing process:
* ``pytest_plugins``: list of named plugins to load
* ``collect_ignore``: list of paths to ignore during test collection, relative to the containing ``conftest.py`` file
* ``rsyncdirs``: list of to-be-rsynced directories for distributed
testing, relative to the containing ``conftest.py`` file.
You may put a conftest.py files in your project root directory or into
your package directory if you want to add project-specific test options.
.. _`specify funcargs`: funcargs.html#application-setup-tutorial-example
.. _`set option defaults`:

View File

@ -1,6 +1,6 @@
import py import py
import os import sys, os
from pytest._core import PluginManager from pytest._core import PluginManager
def pytest_cmdline_parse(pluginmanager, args): def pytest_cmdline_parse(pluginmanager, args):
@ -194,14 +194,10 @@ class Conftest(object):
try: try:
return self._conftestpath2mod[conftestpath] return self._conftestpath2mod[conftestpath]
except KeyError: except KeyError:
if not conftestpath.dirpath('__init__.py').check(file=1): pkgpath = conftestpath.pypkgpath()
# HACK: we don't want any "globally" imported conftest.py, if pkgpath is None:
# prone to conflicts and subtle problems _ensure_removed_sysmodule(conftestpath.purebasename)
modname = str(conftestpath).replace('.', conftestpath.sep) self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport()
mod = conftestpath.pyimport(modname=modname)
else:
mod = conftestpath.pyimport()
self._conftestpath2mod[conftestpath] = mod
dirpath = conftestpath.dirpath() dirpath = conftestpath.dirpath()
if dirpath in self._path2confmods: if dirpath in self._path2confmods:
for path, mods in self._path2confmods.items(): for path, mods in self._path2confmods.items():
@ -216,6 +212,11 @@ class Conftest(object):
self._onimport(mod) self._onimport(mod)
return mod return mod
def _ensure_removed_sysmodule(modname):
try:
del sys.modules[modname]
except KeyError:
pass
class CmdOptions(object): class CmdOptions(object):
""" holds cmdline options as attributes.""" """ holds cmdline options as attributes."""

View File

@ -106,6 +106,27 @@ def test_doubledash_not_considered(testdir):
l = conftest.getconftestmodules(None) l = conftest.getconftestmodules(None)
assert len(l) == 0 assert len(l) == 0
def test_conftest_global_import(testdir):
testdir.makeconftest("x=3")
p = testdir.makepyfile("""
import py
from pytest.plugin.config import Conftest
conf = Conftest()
mod = conf.importconftest(py.path.local("conftest.py"))
assert mod.x == 3
import conftest
assert conftest is mod, (conftest, mod)
subconf = py.path.local().ensure("sub", "conftest.py")
subconf.write("y=4")
mod2 = conf.importconftest(subconf)
assert mod != mod2
assert mod2.y == 4
import conftest
assert conftest is mod2, (conftest, mod)
""")
res = testdir.runpython(p)
assert res.ret == 0
def test_conftestcutdir(testdir): def test_conftestcutdir(testdir):
conf = testdir.makeconftest("") conf = testdir.makeconftest("")
p = testdir.mkdir("x") p = testdir.mkdir("x")