Возможно ли реализовать проект Python с файловой структурой, подобной следующей?
myproj
├── a.py
├── b.py
├── c.py
└── test/
├── a.py
├── b.py
└── c.py
Обратите внимание, в частности, что сценарии тестирования в test/
имеют те же базовые имена, что и файлы модулей, которые они проверяют 1 . (Другими словами, test/a.py
содержит модульные тесты для a.py
; test/b.py
содержит тесты для b.py
и т. Д.)
Тесты по test/
все импортируют unittest
и определяют подклассы unittest.TestCase
.
Я хочу знать, как запускать тесты под test/
, как по отдельности, так и все вместе.
Я перепробовал множество вариантов python -m unittest ...
, но все они либо терпят неудачу (примеры ниже), либо заканчивают тем, что запускают нулевые тесты.
Например,
% python -m unittest test.a
Traceback (most recent call last):
File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
main(module=None)
File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
self.parseArgs(argv)
File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
self.createTests()
File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
self.module)
File "/usr/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
suites = [self.loadTestsFromName(name, module) for name in names]
File "/usr/lib/python2.7/unittest/loader.py", line 100, in loadTestsFromName
parent, obj = obj, getattr(obj, part)
AttributeError: 'module' object has no attribute 'a'
Если я изменю имя каталога test/
на t/
, то ошибка будет выглядеть так:
% python -m unittest t.a
Traceback (most recent call last):
File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
main(module=None)
File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
self.parseArgs(argv)
File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
self.createTests()
File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
self.module)
File "/usr/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
suites = [self.loadTestsFromName(name, module) for name in names]
File "/usr/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
module = __import__('.'.join(parts_copy))
ImportError: No module named t
Или
% python -m unittest t/a.py
Traceback (most recent call last):
File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/usr/lib/python2.7/unittest/__main__.py", line 12, in <module>
main(module=None)
File "/usr/lib/python2.7/unittest/main.py", line 94, in __init__
self.parseArgs(argv)
File "/usr/lib/python2.7/unittest/main.py", line 149, in parseArgs
self.createTests()
File "/usr/lib/python2.7/unittest/main.py", line 158, in createTests
self.module)
File "/usr/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
suites = [self.loadTestsFromName(name, module) for name in names]
File "/usr/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
module = __import__('.'.join(parts_copy))
ImportError: Import by filename is not supported.
(я использую Python 2.7.9.)
UPDATE
Поскольку я назначил награду за этот вопрос, я очень четко скажу, что будет приемлемым ответом.
Любое из следующих действий будет приемлемым:
Как вызвать unittest
из командной строки для запуска отдельных тестов или всех тестов в каталоге test/
; для решения допустимо вносить небольшие изменения в код в сценариях тестирования (например, изменения в операторах импорта).
Если структура файла, показанная выше, по какой-либо причине невозможна, подробное объяснение будет приемлемым решением.
В качестве базового варианта начните со следующего минимального случая со следующей структурой файла:
myproj
├── a.py
├── b.py
└── test/
├── a.py
└── b.py
... и следующее содержание
# a.py
def hello():
print 'hello world'
# b.py
def bye():
print 'good-bye world'
# test/a.py
import unittest
import a
class TestA(unittest.TestCase):
def test_hello(self):
self.assertEqual(a.hello(), None)
# test/b.py
import unittest
import b
class TestB(unittest.TestCase):
def test_bye(self):
self.assertEqual(b.bye(), None)
Покажите, как нужно unittest
запустить тест test/a.py
и как запустить "все тесты в test
". (Последний должен продолжать работать, даже если новые сценарии тестирования добавляются в test
или некоторые текущие сценарии тестирования удаляются.)
Минимальные тесты предложений, предложенных до сих пор, показывают, что они не работают. Например:
% python -m unittest discover -s test -p '*.py'
EE
======================================================================
ERROR: test_hello (a.TestA)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/SHIVAMJINDAL/myproj/test/a.py", line 6, in test_hello
self.assertEqual(a.hello(), None)
AttributeError: 'module' object has no attribute 'hello'
======================================================================
ERROR: test_bye (b.TestB)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/SHIVAMJINDAL/myproj/test/b.py", line 6, in test_bye
self.assertEqual(b.bye, None)
AttributeError: 'module' object has no attribute 'bye'
----------------------------------------------------------------------
Ran 2 tests in 0.000s
FAILED (errors=2)
% tree .
.
├── a.py
├── b.py
├── __init__.py
└── test/
├── a.py
├── b.py
└── __init__.py
1 directory, 6 files
1 Это ограничение носит преднамеренный характер и является неотъемлемой частью представленной здесь проблемы. (Итак, «решение», которое влечет за собой ослабление этого ограничения, на самом деле не является решением.)