introduce new discovery mechanism
XXX experiment with using it before introducing it or wait for feature request
This commit is contained in:
parent
03ee8b7fe0
commit
4cb2c74159
|
@ -258,6 +258,7 @@ class Config(object):
|
||||||
self.trace = self.pluginmanager.trace.root.get("config")
|
self.trace = self.pluginmanager.trace.root.get("config")
|
||||||
self._conftest = Conftest(onimport=self._onimportconftest)
|
self._conftest = Conftest(onimport=self._onimportconftest)
|
||||||
self.hook = self.pluginmanager.hook
|
self.hook = self.pluginmanager.hook
|
||||||
|
self._inicache = {}
|
||||||
|
|
||||||
def _onimportconftest(self, conftestmodule):
|
def _onimportconftest(self, conftestmodule):
|
||||||
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
self.trace("loaded conftestmodule %r" %(conftestmodule,))
|
||||||
|
@ -332,6 +333,13 @@ class Config(object):
|
||||||
""" return configuration value from an ini file. If the
|
""" return configuration value from an ini file. If the
|
||||||
specified name hasn't been registered through a prior ``parse.addini``
|
specified name hasn't been registered through a prior ``parse.addini``
|
||||||
call (usually from a plugin), a ValueError is raised. """
|
call (usually from a plugin), a ValueError is raised. """
|
||||||
|
try:
|
||||||
|
return self._inicache[name]
|
||||||
|
except KeyError:
|
||||||
|
self._inicache[name] = val = self._getini(name)
|
||||||
|
return val
|
||||||
|
|
||||||
|
def _getini(self, name):
|
||||||
try:
|
try:
|
||||||
description, type, default = self._parser._inidict[name]
|
description, type, default = self._parser._inidict[name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
|
|
@ -13,6 +13,13 @@ def pytest_addoption(parser):
|
||||||
group.addoption('--funcargs',
|
group.addoption('--funcargs',
|
||||||
action="store_true", dest="showfuncargs", default=False,
|
action="store_true", dest="showfuncargs", default=False,
|
||||||
help="show available function arguments, sorted by plugin")
|
help="show available function arguments, sorted by plugin")
|
||||||
|
parser.addini("python_files", type="args",
|
||||||
|
default=('test_*.py', '*_test.py'),
|
||||||
|
help="glob-style file patterns for Python test module discovery")
|
||||||
|
parser.addini("python_classes", type="args", default=("Test",),
|
||||||
|
help="prefixes for Python test class discovery")
|
||||||
|
parser.addini("python_functions", type="args", default=("test",),
|
||||||
|
help="prefixes for Python test function and method discovery")
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
if config.option.showfuncargs:
|
if config.option.showfuncargs:
|
||||||
|
@ -46,8 +53,13 @@ def pytest_pyfunc_call(__multicall__, pyfuncitem):
|
||||||
def pytest_collect_file(path, parent):
|
def pytest_collect_file(path, parent):
|
||||||
ext = path.ext
|
ext = path.ext
|
||||||
pb = path.purebasename
|
pb = path.purebasename
|
||||||
if ext == ".py" and (pb.startswith("test_") or pb.endswith("_test") or
|
if ext == ".py":
|
||||||
parent.session.isinitpath(path)):
|
if not parent.session.isinitpath(path):
|
||||||
|
for pat in parent.config.getini('python_files'):
|
||||||
|
if path.fnmatch(pat):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return
|
||||||
return parent.ihook.pytest_pycollect_makemodule(
|
return parent.ihook.pytest_pycollect_makemodule(
|
||||||
path=path, parent=parent)
|
path=path, parent=parent)
|
||||||
|
|
||||||
|
@ -145,9 +157,14 @@ class PyobjMixin(object):
|
||||||
class PyCollectorMixin(PyobjMixin, pytest.Collector):
|
class PyCollectorMixin(PyobjMixin, pytest.Collector):
|
||||||
|
|
||||||
def funcnamefilter(self, name):
|
def funcnamefilter(self, name):
|
||||||
return name.startswith('test')
|
for prefix in self.config.getini("python_functions"):
|
||||||
|
if name.startswith(prefix):
|
||||||
|
return True
|
||||||
|
|
||||||
def classnamefilter(self, name):
|
def classnamefilter(self, name):
|
||||||
return name.startswith('Test')
|
for prefix in self.config.getini("python_classes"):
|
||||||
|
if name.startswith(prefix):
|
||||||
|
return True
|
||||||
|
|
||||||
def collect(self):
|
def collect(self):
|
||||||
# NB. we avoid random getattrs and peek in the __dict__ instead
|
# NB. we avoid random getattrs and peek in the __dict__ instead
|
||||||
|
|
|
@ -86,3 +86,18 @@ builtin configuration file options
|
||||||
This would tell py.test to not recurse into typical subversion or
|
This would tell py.test to not recurse into typical subversion or
|
||||||
sphinx-build directories or into any ``tmp`` prefixed directory.
|
sphinx-build directories or into any ``tmp`` prefixed directory.
|
||||||
|
|
||||||
|
.. confval:: python_discovery
|
||||||
|
|
||||||
|
Determines names and patterns for finding Python tests, specified
|
||||||
|
through indendent ``name = value`` settings with these possible names::
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
python_discovery =
|
||||||
|
file = <glob-pattern> <glob-pattern>
|
||||||
|
func = <function-or-method-name-prefix>
|
||||||
|
class = <class-name-prefix>
|
||||||
|
|
||||||
|
See :ref:`change naming conventions` for examples. the ``class``
|
||||||
|
to be empty in which case all non-underscore empty classes
|
||||||
|
will be considered.
|
||||||
|
|
||||||
|
|
|
@ -12,19 +12,63 @@ You can set the :confval:`norecursedirs` option in an ini-file, for example your
|
||||||
|
|
||||||
This would tell py.test to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
|
This would tell py.test to not recurse into typical subversion or sphinx-build directories or into any ``tmp`` prefixed directory.
|
||||||
|
|
||||||
always try to interpret arguments as Python packages
|
.. _`change naming conventions`:
|
||||||
|
|
||||||
|
change naming conventions
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
You can configure different naming conventions by setting
|
||||||
|
the :confval:`pytest_pycollect` configuration option. Example::
|
||||||
|
|
||||||
|
# content of setup.cfg
|
||||||
|
# can also be defined in in tox.ini or pytest.ini file
|
||||||
|
[pytest]
|
||||||
|
python_files=check_*.py
|
||||||
|
python_classes=Check
|
||||||
|
python_functions=check
|
||||||
|
|
||||||
|
This would make py.test look for ``check_`` prefixes in
|
||||||
|
Python filenames, ``Check`` prefixes in classes and ``check`` prefixes
|
||||||
|
in functions and classes. For example, if we have::
|
||||||
|
|
||||||
|
# content of check_myapp.py
|
||||||
|
class CheckMyApp:
|
||||||
|
def check_simple(self):
|
||||||
|
pass
|
||||||
|
def check_complex(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
then the test collection looks like this::
|
||||||
|
|
||||||
|
$ py.test --collectonly
|
||||||
|
<Module 'check_myapp.py'>
|
||||||
|
<Class 'CheckMyApp'>
|
||||||
|
<Instance '()'>
|
||||||
|
<Function 'check_simple'>
|
||||||
|
<Function 'check_complex'>
|
||||||
|
|
||||||
|
interpret cmdline arguments as Python packages
|
||||||
-----------------------------------------------------
|
-----------------------------------------------------
|
||||||
|
|
||||||
You can use the ``--pyargs`` option to make py.test try
|
You can use the ``--pyargs`` option to make py.test try
|
||||||
interpreting arguments as python package names, deriving
|
interpreting arguments as python package names, deriving
|
||||||
their file system path and then running the test. Through
|
their file system path and then running the test. For
|
||||||
an ini-file and the :confval:`addopts` option you can make
|
example if you have unittest2 installed you can type::
|
||||||
this change more permanently::
|
|
||||||
|
py.test --pyargs unittest2.test.test_skipping -q
|
||||||
|
|
||||||
|
which will run the respective test module. Like with
|
||||||
|
other options, through an ini-file and the :confval:`addopts` option you
|
||||||
|
can make this change more permanently::
|
||||||
|
|
||||||
# content of pytest.ini
|
# content of pytest.ini
|
||||||
[pytest]
|
[pytest]
|
||||||
addopts = --pyargs
|
addopts = --pyargs
|
||||||
|
|
||||||
|
Now a simple invocation of ``py.test NAME`` will check
|
||||||
|
if NAME exists as an importable package/module and otherwise
|
||||||
|
treat it as a filesystem path.
|
||||||
|
|
||||||
finding out what is collected
|
finding out what is collected
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1208,3 +1208,32 @@ class TestRaises:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_customized_python_discovery(testdir):
|
||||||
|
testdir.makeini("""
|
||||||
|
[pytest]
|
||||||
|
python_files=check_*.py
|
||||||
|
python_classes=Check
|
||||||
|
python_functions=check
|
||||||
|
""")
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
def check_simple():
|
||||||
|
pass
|
||||||
|
class CheckMyApp:
|
||||||
|
def check_meth(self):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
p2 = p.new(basename=p.basename.replace("test", "check"))
|
||||||
|
p.move(p2)
|
||||||
|
result = testdir.runpytest("--collectonly", "-s")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*check_customized*",
|
||||||
|
"*check_simple*",
|
||||||
|
"*CheckMyApp*",
|
||||||
|
"*check_meth*",
|
||||||
|
])
|
||||||
|
|
||||||
|
result = testdir.runpytest()
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*2 passed*",
|
||||||
|
])
|
||||||
|
|
3
tox.ini
3
tox.ini
|
@ -73,3 +73,6 @@ minversion=2.0
|
||||||
plugins=pytester
|
plugins=pytester
|
||||||
addopts= -rxf --pyargs --doctest-modules
|
addopts= -rxf --pyargs --doctest-modules
|
||||||
rsyncdirs=tox.ini pytest.py _pytest testing
|
rsyncdirs=tox.ini pytest.py _pytest testing
|
||||||
|
python_files=test_*.py *_test.py
|
||||||
|
python_classes=Test Acceptance
|
||||||
|
python_functions=test
|
||||||
|
|
Loading…
Reference in New Issue