Merged in nicoddemus/pytest/python-classes-glob (pull request #225)

added support for glob-style patterns to python_classes and python_functions config options
This commit is contained in:
holger krekel 2014-10-22 07:14:10 +02:00
commit eac4514227
5 changed files with 81 additions and 24 deletions

View File

@ -16,6 +16,9 @@
parameter is a callable, you also need to pass in a reason to disambiguate parameter is a callable, you also need to pass in a reason to disambiguate
it from the "decorator" case. Thanks Tom Viner. it from the "decorator" case. Thanks Tom Viner.
- "python_classes" and "python_functions" options now support glob-patterns
for test discovery, as discussed in issue600. Thanks Ldiary Translations.
2.6.4.dev 2.6.4.dev
---------- ----------
@ -86,7 +89,7 @@
Thanks sontek. Thanks sontek.
- Implement issue549: user-provided assertion messages now no longer - Implement issue549: user-provided assertion messages now no longer
replace the py.test instrospection message but are shown in addition replace the py.test introspection message but are shown in addition
to them. to them.
2.6.1 2.6.1

View File

@ -1,4 +1,5 @@
""" Python test discovery, setup and run of test functions. """ """ Python test discovery, setup and run of test functions. """
import fnmatch
import py import py
import inspect import inspect
import sys import sys
@ -127,9 +128,10 @@ def pytest_addoption(parser):
default=['test_*.py', '*_test.py'], default=['test_*.py', '*_test.py'],
help="glob-style file patterns for Python test module discovery") help="glob-style file patterns for Python test module discovery")
parser.addini("python_classes", type="args", default=["Test",], parser.addini("python_classes", type="args", default=["Test",],
help="prefixes for Python test class discovery") help="prefixes or glob names for Python test class discovery")
parser.addini("python_functions", type="args", default=["test",], parser.addini("python_functions", type="args", default=["test",],
help="prefixes for Python test function and method discovery") help="prefixes or glob names for Python test function and "
"method discovery")
def pytest_cmdline_main(config): def pytest_cmdline_main(config):
if config.option.showfixtures: if config.option.showfixtures:
@ -307,14 +309,26 @@ class PyobjMixin(PyobjContext):
class PyCollector(PyobjMixin, pytest.Collector): class PyCollector(PyobjMixin, pytest.Collector):
def funcnamefilter(self, name): def funcnamefilter(self, name):
for prefix in self.config.getini("python_functions"): return self._matches_prefix_or_glob_option('python_functions', name)
if name.startswith(prefix):
return True
def classnamefilter(self, name): def classnamefilter(self, name):
for prefix in self.config.getini("python_classes"): return self._matches_prefix_or_glob_option('python_classes', name)
if name.startswith(prefix):
def _matches_prefix_or_glob_option(self, option_name, name):
"""
checks if the given name matches the prefix or glob-pattern defined
in ini configuration.
"""
for option in self.config.getini(option_name):
if name.startswith(option):
return True return True
# check that name looks like a glob-string before calling fnmatch
# because this is called for every name in each collected module,
# and fnmatch is somewhat expensive to call
elif ('*' in option or '?' in option or '[' in option) and \
fnmatch.fnmatch(name, option):
return True
return False
def collect(self): def collect(self):
if not getattr(self.obj, "__test__", True): if not getattr(self.obj, "__test__", True):

View File

@ -115,17 +115,33 @@ Builtin configuration file options
.. confval:: python_classes .. confval:: python_classes
One or more name prefixes determining which test classes One or more name prefixes or glob-style patterns determining which classes
are considered as test modules. are considered for test collection. Here is an example of how to collect
tests from classes that end in ``Suite``::
# content of pytest.ini
[pytest]
python_classes = *Suite
Note that ``unittest.TestCase`` derived classes are always collected
regardless of this option, as ``unittest``'s own collection framework is used
to collect those tests.
.. confval:: python_functions .. confval:: python_functions
One or more name prefixes determining which test functions One or more name prefixes or glob-patterns determining which test functions
and methods are considered as test modules. Note that this and methods are considered tests. Here is an example of how
has no effect on methods that live on a ``unittest.TestCase`` to collect test functions and methods that end in ``_test``::
derived class.
See :ref:`change naming conventions` for examples. # content of pytest.ini
[pytest]
python_functions = *_test
Note that this has no effect on methods that live on a ``unittest
.TestCase`` derived class, as ``unittest``'s own collection framework is used
to collect those tests.
See :ref:`change naming conventions` for more detailed examples.
.. confval:: doctest_optionflags .. confval:: doctest_optionflags

View File

@ -26,17 +26,17 @@ the :confval:`python_files`, :confval:`python_classes` and
[pytest] [pytest]
python_files=check_*.py python_files=check_*.py
python_classes=Check python_classes=Check
python_functions=check python_functions=*_check
This would make ``pytest`` look for ``check_`` prefixes in This would make ``pytest`` look for tests in files that match the ``check_*
Python filenames, ``Check`` prefixes in classes and ``check`` prefixes .py`` glob-pattern, ``Check`` prefixes in classes, and functions and methods
in functions and classes. For example, if we have:: that match ``*_check``. For example, if we have::
# content of check_myapp.py # content of check_myapp.py
class CheckMyApp: class CheckMyApp:
def check_simple(self): def simple_check(self):
pass pass
def check_complex(self): def complex_check(self):
pass pass
then the test collection looks like this:: then the test collection looks like this::
@ -48,14 +48,14 @@ then the test collection looks like this::
<Module 'check_myapp.py'> <Module 'check_myapp.py'>
<Class 'CheckMyApp'> <Class 'CheckMyApp'>
<Instance '()'> <Instance '()'>
<Function 'check_simple'> <Function 'simple_check'>
<Function 'check_complex'> <Function 'complex_check'>
============================= in 0.01 seconds ============================= ============================= in 0.01 seconds =============================
.. note:: .. note::
the ``python_functions`` and ``python_classes`` has no effect the ``python_functions`` and ``python_classes`` options has no effect
for ``unittest.TestCase`` test discovery because pytest delegates for ``unittest.TestCase`` test discovery because pytest delegates
detection of test case methods to unittest code. detection of test case methods to unittest code.

View File

@ -528,6 +528,30 @@ class Test_genitems:
assert s.endswith("test_example_items1.testone") assert s.endswith("test_example_items1.testone")
print(s) print(s)
def test_class_and_functions_discovery_using_glob(self, testdir):
"""
tests that python_classes and python_functions config options work
as prefixes and glob-like patterns (issue #600).
"""
testdir.makeini("""
[pytest]
python_classes = *Suite Test
python_functions = *_test test
""")
p = testdir.makepyfile('''
class MyTestSuite:
def x_test(self):
pass
class TestCase:
def test_y(self):
pass
''')
items, reprec = testdir.inline_genitems(p)
ids = [x.getmodpath() for x in items]
assert ids == ['MyTestSuite.x_test', 'TestCase.test_y']
def test_matchnodes_two_collections_same_file(testdir): def test_matchnodes_two_collections_same_file(testdir):
testdir.makeconftest(""" testdir.makeconftest("""
import pytest import pytest