Issue 10620: Specifying test modules by path instead of module name to 'python -m unittest'
This commit is contained in:
parent
e2bb4eb77b
commit
37d120aeb4
|
@ -204,6 +204,16 @@ modules, classes or even individual test methods::
|
|||
You can pass in a list with any combination of module names, and fully
|
||||
qualified class or method names.
|
||||
|
||||
Test modules can be specified by file path as well::
|
||||
|
||||
python -m unittest tests/test_something.py
|
||||
|
||||
This allows you to use the shell filename completion to specify the test module.
|
||||
The file specified must still be importable as a module. The path is converted
|
||||
to a module name by removing the '.py' and converting path separators into '.'.
|
||||
If you want to execute a test file that isn't importable as a module you should
|
||||
execute the file directly instead.
|
||||
|
||||
You can run tests with more detail (higher verbosity) by passing in the -v flag::
|
||||
|
||||
python -m unittest -v test_module
|
||||
|
|
|
@ -58,7 +58,24 @@ Examples:
|
|||
in MyTestCase
|
||||
"""
|
||||
|
||||
def _convert_name(name):
|
||||
# on Linux / Mac OS X 'foo.PY' is not importable, but on
|
||||
# Windows it is. Simpler to do a case insensitive match
|
||||
# a better check would be to check that the name is a
|
||||
# valid Python module name.
|
||||
if os.path.isfile(name) and name.lower().endswith('.py'):
|
||||
if os.path.isabs(name):
|
||||
rel_path = os.path.relpath(name, os.getcwd())
|
||||
if os.path.isabs(rel_path) or rel_path.startswith(os.pardir):
|
||||
return name
|
||||
name = rel_path
|
||||
# on Windows both '\' and '/' are used as path
|
||||
# separators. Better to replace both than rely on os.path.sep
|
||||
return name[:-3].replace('\\', '.').replace('/', '.')
|
||||
return name
|
||||
|
||||
def _convert_names(names):
|
||||
return [_convert_name(name) for name in names]
|
||||
|
||||
class TestProgram(object):
|
||||
"""A command-line program that runs a set of tests; this is primarily
|
||||
|
@ -153,7 +170,7 @@ class TestProgram(object):
|
|||
# createTests will load tests from self.module
|
||||
self.testNames = None
|
||||
elif len(args) > 0:
|
||||
self.testNames = args
|
||||
self.testNames = _convert_names(args)
|
||||
if __name__ == '__main__':
|
||||
# to support python -m unittest ...
|
||||
self.module = None
|
||||
|
|
|
@ -273,6 +273,85 @@ class TestCommandLineArgs(unittest.TestCase):
|
|||
program.runTests()
|
||||
self.assertTrue(self.installed)
|
||||
|
||||
def _patch_isfile(self, names, exists=True):
|
||||
def isfile(path):
|
||||
return path in names
|
||||
original = os.path.isfile
|
||||
os.path.isfile = isfile
|
||||
def restore():
|
||||
os.path.isfile = original
|
||||
self.addCleanup(restore)
|
||||
|
||||
|
||||
def testParseArgsFileNames(self):
|
||||
# running tests with filenames instead of module names
|
||||
program = self.program
|
||||
argv = ['progname', 'foo.py', 'bar.Py', 'baz.PY', 'wing.txt']
|
||||
self._patch_isfile(argv)
|
||||
|
||||
program.createTests = lambda: None
|
||||
program.parseArgs(argv)
|
||||
|
||||
# note that 'wing.txt' is not a Python file so the name should
|
||||
# *not* be converted to a module name
|
||||
expected = ['foo', 'bar', 'baz', 'wing.txt']
|
||||
self.assertEqual(program.testNames, expected)
|
||||
|
||||
|
||||
def testParseArgsFilePaths(self):
|
||||
program = self.program
|
||||
argv = ['progname', 'foo/bar/baz.py', 'green\\red.py']
|
||||
self._patch_isfile(argv)
|
||||
|
||||
program.createTests = lambda: None
|
||||
program.parseArgs(argv)
|
||||
|
||||
expected = ['foo.bar.baz', 'green.red']
|
||||
self.assertEqual(program.testNames, expected)
|
||||
|
||||
|
||||
def testParseArgsNonExistentFiles(self):
|
||||
program = self.program
|
||||
argv = ['progname', 'foo/bar/baz.py', 'green\\red.py']
|
||||
self._patch_isfile([])
|
||||
|
||||
program.createTests = lambda: None
|
||||
program.parseArgs(argv)
|
||||
|
||||
self.assertEqual(program.testNames, argv[1:])
|
||||
|
||||
def testParseArgsAbsolutePathsThatCanBeConverted(self):
|
||||
cur_dir = os.getcwd()
|
||||
program = self.program
|
||||
def _join(name):
|
||||
return os.path.join(cur_dir, name)
|
||||
argv = ['progname', _join('foo/bar/baz.py'), _join('green\\red.py')]
|
||||
self._patch_isfile(argv)
|
||||
|
||||
program.createTests = lambda: None
|
||||
program.parseArgs(argv)
|
||||
|
||||
expected = ['foo.bar.baz', 'green.red']
|
||||
self.assertEqual(program.testNames, expected)
|
||||
|
||||
def testParseArgsAbsolutePathsThatCannotBeConverted(self):
|
||||
program = self.program
|
||||
# will this test work on Windows? (is '/...' considered absolute?)
|
||||
argv = ['progname', '/foo/bar/baz.py', '/green/red.py']
|
||||
self._patch_isfile(argv)
|
||||
|
||||
program.createTests = lambda: None
|
||||
program.parseArgs(argv)
|
||||
|
||||
self.assertEqual(program.testNames, argv[1:])
|
||||
|
||||
# it may be better to use platform specific functions to normalise paths
|
||||
# rather than accepting '.PY' and '\' as file seprator on Linux / Mac
|
||||
# it would also be better to check that a filename is a valid module
|
||||
# identifier (we have a regex for this in loader.py)
|
||||
# for invalid filenames should we raise a useful error rather than
|
||||
# leaving the current error message (import of filename fails) in place?
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
11
Misc/NEWS
11
Misc/NEWS
|
@ -37,6 +37,13 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue 10620: `python -m unittest` can accept file paths instead of module
|
||||
names for running specific tests.
|
||||
|
||||
- Issue #9424: Deprecate the `unittest.TestCase` methods `assertEquals`,
|
||||
`assertNotEquals`, `assertAlmostEquals`, `assertNotAlmostEquals` and `assert_`
|
||||
and replace them with the correct methods in the Python test suite.
|
||||
|
||||
- Issue #10272: The ssl module now raises socket.timeout instead of a generic
|
||||
SSLError on socket timeouts.
|
||||
|
||||
|
@ -236,10 +243,6 @@ Tests
|
|||
- `python -m test` can be used to run the test suite as well as
|
||||
`python -m test.regrtest`.
|
||||
|
||||
- Issue #9424: Deprecate the `unittest.TestCase` methods `assertEquals`,
|
||||
`assertNotEquals`, `assertAlmostEquals`, `assertNotAlmostEquals` and `assert_`
|
||||
and replace them with the correct methods in the Python test suite.
|
||||
|
||||
- Do not fail test_socket when the IP address of the local hostname cannot be
|
||||
looked up.
|
||||
|
||||
|
|
Loading…
Reference in New Issue