Attached is a patch that implements test discovery for unittest.
It includes command line argument handling (awkward manual handling but
works fine...), so that it can be invoked through:
python -m unittest discover
python -m unittest discover start_dir pattern top_level_dir
If the patch is acceptable then I will write docs. Do I need to make a
post to Python-dev or is it ok to have it reviewed here? The basic
mechanism was hammered out on the Testing-in-Python mailing list with a
lot of input from Robert Collins.
Brett Cannon has already looked over the test discovery code (not the
command line handling code though!).
It includes a customization hook both for modules and packages to
customize test loading and discovery. Described below:
Advantage of this: it is simple (easy to extend)
Disadvantage: it is simple (a lot it doesn't do)
Test discovery is provided through the TestLoader.discover method.
It takes three arguments - start directory, pattern to match test files
(defaults to 'test*.py') and the top level directory of the project
(defaults to the start directory).
The main restriction is that all your tests must be importable from the
top level directory of your project. The start directory is then
recursively searched for files and packages that match the pattern you
pass in. Tests are loaded from matching modules, and all tests run.
(A further restriction is that discovery only currently recognises
packages as directories containing __init__.py files and not .pyc etc.)
The load_tests protocol provides a way for packages and modules to
customize test loading. This is inspired by a similar system in use in
the Bzr test suite.
Iff a test module defines a load_tests function then
TestLoader.loadTestsFromModule will call this function with loader,
tests, None. This should return a suite.
An example 'do nothing' implementation of load_tests would be:
def load_tests(loader, tests, pattern):
return tests
If a package directory name matches the pattern you pass into discovery
and the __init__.py contains a load_tests function then it will be
called with loader, tests, pattern. No further discovery will be done
into the package, but because it is passed the pattern as an argument it
is free to continue discovery itself. A do nothing load_tests for a
package is:
def load_tests(loader, tests, pattern):
if pattern is None:
return tests
return TestSuite(tests,
loader.discover(os.path.dirname(os.path.abspath(__file__)), pattern))
(The loader stores the top level directory it was originally called with
specifically for this use case. load_tests should not pass in a new top
level directory to the existing loader but create a new loader if it
wants to do this.)
Discovery does not follow the __path__ attribute of packages / modules
and only looks at the filesystem.
I have tested this implementation on the importlib tests (as one
example) and it worked fine.
Many of the restrictions mentioned here would be very easy to solve in
the future, but I think it is important to get some simple version of
test discovery in ASAP.
All I can think of for now...