我正在试图找出如何让python setup.py test
运行等效的python -m unittest discover
。我不想使用run_tests.py脚本,我不想使用任何外部测试工具(如nose
或py.test
)。如果解决方案仅适用于python 2.7,则可以。
在setup.py
中,我认为我需要在配置中向test_suite
和/或test_loader
字段添加内容,但我似乎无法找到正常工作的组合:
config = {
'name': name,
'version': version,
'url': url,
'test_suite': '???',
'test_loader': '???',
}
仅使用内置于python 2.7中的unittest
是否可行?
仅供参考,我的项目结构如下:
project/
package/
__init__.py
module.py
tests/
__init__.py
test_module.py
run_tests.py <- I want to delete this
setup.py
更新:这可以通过unittest2
实现,但我希望仅使用unittest
找到相同的内容
来自https://pypi.python.org/pypi/unittest2
unittest2包括一个非常基本的setuptools兼容测试收集器。在setup.py中指定test_suite ='unittest2.collector'。这将使用包含setup.py的目录中的默认参数启动测试发现,因此它可能是最有用的示例(请参阅unittest2 / collector.py)。
现在,我只是使用一个名为run_tests.py
的脚本,但我希望通过转移到仅使用python setup.py test
的解决方案来解决这个问题。
这是{I}我希望删除的run_tests.py
:
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)
答案 0 :(得分:37)
如果使用py27 +或py32 +,解决方案非常简单:
test_suite="tests",
答案 1 :(得分:34)
来自Building and Distributing Packages with Setuptools(强调我的):
test_suite
命名unittest.TestCase子类(或包或模块)的字符串 包含它们中的一个或多个,或这种子类的方法,或命名 一个可以不带参数调用的函数,并返回一个unittest.TestSuite 。
因此,在setup.py
中,您将添加一个返回TestSuite的函数:
import unittest
def my_test_suite():
test_loader = unittest.TestLoader()
test_suite = test_loader.discover('tests', pattern='test_*.py')
return test_suite
然后,您将指定命令setup
,如下所示:
setup(
...
test_suite='setup.my_test_suite',
...
)
答案 2 :(得分:17)
您不需要配置即可实现此功能。基本上有两种方法可以做到:
快捷方式
将test_module.py
重命名为module_test.py
(基本上将_test
添加为测试特定模块的后缀),python会自动找到它。只需确保将其添加到setup.py
:
from setuptools import setup, find_packages
setup(
...
test_suite = 'tests',
...
)
很长的路
以下是使用当前目录结构的方法:
project/
package/
__init__.py
module.py
tests/
__init__.py
test_module.py
run_tests.py <- I want to delete this
setup.py
在tests/__init__.py
下,您要导入unittest
和单元测试脚本test_module
,然后创建一个运行测试的函数。在tests/__init__.py
中,输入以下内容:
import unittest
import test_module
def my_module_suite():
loader = unittest.TestLoader()
suite = loader.loadTestsFromModule(test_module)
return suite
除了TestLoader
之外,loadTestsFromModule
类还有其他功能。你可以运行dir(unittest.TestLoader)
来查看其他的,但这个是最简单的。
由于您的目录结构如此,您可能希望test_module
能够导入您的module
脚本。您可能已经这样做了,但是如果您没有这样做,您可以包含父路径,以便您可以导入package
模块和module
脚本。在test_module.py
的顶部,输入:
import os, sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import unittest
import package.module
...
最后,在setup.py
中,包含tests
模块并运行您创建的命令my_module_suite
:
from setuptools import setup, find_packages
setup(
...
test_suite = 'tests.my_module_suite',
...
)
然后您只需运行python setup.py test
。
以下是sample某人作为参考。
答案 3 :(得分:4)
一种可能的解决方案是简单地扩展test
和distutils
/ setuptools
的{{1}}命令。这似乎是一个比我想要的更复杂的方法,但似乎在运行distribute
时正确地发现并运行了我的包中的所有测试。我暂时选择这个作为我的问题的答案,希望有人能提供更优雅的解决方案:)
示例python setup.py test
:
setup.py
答案 4 :(得分:3)
Python的标准库unittest
模块支持发现(在Python 2.7及更高版本以及Python 3.2及更高版本中)。如果您可以假设这些最低版本,那么您只需将discover
命令行参数添加到unittest
命令。
setup.py
只需要进行一些小调整:
import setuptools.command.test
from setuptools import (find_packages, setup)
class TestCommand(setuptools.command.test.test):
""" Setuptools test command explicitly using test discovery. """
def _test_args(self):
yield 'discover'
for arg in super(TestCommand, self)._test_args():
yield arg
setup(
...
cmdclass={
'test': TestCommand,
},
)
答案 5 :(得分:2)
另一个不太理想的解决方案,受到http://hg.python.org/unittest2/file/2b6411b9a838/unittest2/collector.py
的启发添加一个返回TestSuite
个已发现测试的模块。然后配置设置以调用该模块。
project/
package/
__init__.py
module.py
tests/
__init__.py
test_module.py
discover_tests.py
setup.py
这是discover_tests.py
:
import os
import sys
import unittest
def additional_tests():
setup_file = sys.modules['__main__'].__file__
setup_dir = os.path.abspath(os.path.dirname(setup_file))
return unittest.defaultTestLoader.discover(setup_dir)
这里是setup.py
:
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
config = {
'name': 'name',
'version': 'version',
'url': 'http://example.com',
'test_suite': 'discover_tests',
}
setup(**config)
答案 6 :(得分:0)
这不会删除run_tests.py,但会使其与setuptools一起使用。添加:
class Loader(unittest.TestLoader):
def loadTestsFromNames(self, names, _=None):
return self.discover(names[0])
然后在setup.py中:(我假设你正在做setup(**config)
之类的事情)
config = {
...
'test_loader': 'run_tests:Loader',
'test_suite': '.', # your start_dir for discover()
}
我看到的唯一缺点是它弯曲了loadTestsFromNames
的语义,但是setuptools test命令是唯一的消费者,并在specified way中调用它。