I'm trying to figure out how to get python setup.py test
to run the equivalent of python -m unittest discover
. I don't want to use a run_tests.py script and I don't want to use any external test tools (like nose
or py.test
). It's OK if the solution only works on python 2.7.
In setup.py
, I think I need to add something to the test_suite
and/or test_loader
fields in config, but I can't seem to find a combination that works correctly:
config = {
'name': name,
'version': version,
'url': url,
'test_suite': '???',
'test_loader': '???',
}
Is this possible using only unittest
built into python 2.7?
FYI, my project structure looks like this:
project/
package/
__init__.py
module.py
tests/
__init__.py
test_module.py
run_tests.py <- I want to delete this
setup.py
Update: This is possible with unittest2
but I want find something equivalent using only unittest
From https://pypi.python.org/pypi/unittest2
unittest2 includes a very basic setuptools compatible test collector. Specify test_suite = 'unittest2.collector' in your setup.py. This starts test discovery with the default parameters from the directory containing setup.py, so it is perhaps most useful as an example (see unittest2/collector.py).
For now, I'm just using a script called run_tests.py
, but I'm hoping I can get rid of this by moving to a solution that only uses python setup.py test
.
Here's the run_tests.py
I'm hoping to remove:
import unittest
if __name__ == '__main__':
# use the default shared TestLoader instance
test_loader = unittest.defaultTestLoader
# use the basic test runner that outputs to sys.stderr
test_runner = unittest.TextTestRunner()
# automatically discover all tests in the current dir of the form test*.py
# NOTE: only works for python 2.7 and later
test_suite = test_loader.discover('.')
# run the test suite
test_runner.run(test_suite)
One possible solution is to simply extend the
test
command fordistutils
andsetuptools
/distribute
. This seems like a total kluge and way more complicated than I would prefer, but seems to correctly discover and run all the tests in my package upon runningpython setup.py test
. I'm holding off on selecting this as the answer to my question in hopes that someone will provide a more elegant solution :)(Inspired by https://docs.pytest.org/en/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner)
Example
setup.py
:Another less than ideal solution slightly inspired by http://hg.python.org/unittest2/file/2b6411b9a838/unittest2/collector.py
Add a module that returns a
TestSuite
of discovered tests. Then configure setup to call that module.Here's
discover_tests.py
:And here's
setup.py
:If you use py27+ or py32+, the solution is pretty simple:
You don't need config to get this working. There are basically two main ways to do it:
The quick way
Rename your
test_module.py
tomodule_test.py
(basically add_test
as a suffix to tests for a particular module), and python will find it automatically. Just make sure to add this tosetup.py
:The long way
Here's how to do it with your current directory structure:
Under
tests/__init__.py
, you want to import theunittest
and your unit test scripttest_module
, and then create a function to run the tests. Intests/__init__.py
, type in something like this:The
TestLoader
class has other functions besidesloadTestsFromModule
. You can rundir(unittest.TestLoader)
to see the other ones, but this one is the simplest to use.Since your directory structure is such, you'll probably want the
test_module
to be able to import yourmodule
script. You might have already done this, but just in case you didn't, you could include the parent path so that you can import thepackage
module and themodule
script. At the top of yourtest_module.py
, type:Then finally, in
setup.py
, include thetests
module and run the command you created,my_module_suite
:Then you just run
python setup.py test
.Here is a sample someone made as a reference.
Python's standard library
unittest
module supports discovery (in Python 2.7 and later, and Python 3.2 and later). If you can assume those minimum versions, then you can just add thediscover
command line argument to theunittest
command.Only a small tweak is needed to
setup.py
:This won't remove run_tests.py, but will make it work with setuptools. Add:
Then in setup.py: (I assume you're doing something like
setup(**config)
)The only downside I see is it's bending the semantics of
loadTestsFromNames
, but the setuptools test command is the only consumer, and calls it in a specified way.