如何从“python setup.py test”运行单元测试发现?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17001010/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to run unittest discover from "python setup.py test"?
提问by cdwilson
I'm trying to figure out how to get python setup.py testto 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 noseor py.test). It's OK if the solution only works on python 2.7.
我试图弄清楚如何python setup.py test运行相当于python -m unittest discover. 我不想使用 run_tests.py 脚本,也不想使用任何外部测试工具(如nose或py.test)。如果该解决方案仅适用于 python 2.7 就可以了。
In setup.py, I think I need to add something to the test_suiteand/or test_loaderfields in config, but I can't seem to find a combination that works correctly:
在 中setup.py,我想我需要在 config 中的test_suite和/或test_loader字段中添加一些内容,但我似乎找不到可以正常工作的组合:
config = {
'name': name,
'version': version,
'url': url,
'test_suite': '???',
'test_loader': '???',
}
Is this possible using only unittestbuilt into python 2.7?
这是否可以仅使用unittest内置于 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 unittest2but I want find something equivalent using only unittest
更新:这是可能的,unittest2但我想找到一些等效的只使用unittest
From https://pypi.python.org/pypi/unittest2
来自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).
unittest2 包括一个非常基本的 setuptools 兼容测试收集器。在 setup.py 中指定 test_suite = 'unittest2.collector'。这将使用包含 setup.py 的目录中的默认参数启动测试发现,因此它可能是最有用的示例(请参阅 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.
目前,我只使用一个名为 的脚本run_tests.py,但我希望通过转向仅使用python setup.py test.
Here's the run_tests.pyI'm hoping to remove:
这是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)
回答by cdwilson
One possible solution is to simply extend the testcommand for distutilsand setuptools/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 running python 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 :)
一种可能的解决方案是简单地扩展和/的test命令。这似乎是一个大杂烩,而且比我希望的要复杂得多,但似乎在运行. 我推迟选择这个作为我问题的答案,希望有人能提供更优雅的解决方案:)distutilssetuptoolsdistributepython setup.py test
(Inspired by https://docs.pytest.org/en/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner)
Example setup.py:
示例setup.py:
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
def discover_and_run_tests():
import os
import sys
import unittest
# get setup.py directory
setup_file = sys.modules['__main__'].__file__
setup_dir = os.path.abspath(os.path.dirname(setup_file))
# 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
# NOTE: only works for python 2.7 and later
test_suite = test_loader.discover(setup_dir)
# run the test suite
test_runner.run(test_suite)
try:
from setuptools.command.test import test
class DiscoverTest(test):
def finalize_options(self):
test.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
discover_and_run_tests()
except ImportError:
from distutils.core import Command
class DiscoverTest(Command):
user_options = []
def initialize_options(self):
pass
def finalize_options(self):
pass
def run(self):
discover_and_run_tests()
config = {
'name': 'name',
'version': 'version',
'url': 'http://example.com',
'cmdclass': {'test': DiscoverTest},
}
setup(**config)
回答by cdwilson
Another less than ideal solution slightly inspired by http://hg.python.org/unittest2/file/2b6411b9a838/unittest2/collector.py
另一个不太理想的解决方案受到http://hg.python.org/unittest2/file/2b6411b9a838/unittest2/collector.py 的启发
Add a module that returns a TestSuiteof discovered tests. Then configure setup to call that module.
添加一个返回已TestSuite发现测试的模块。然后配置 setup 以调用该模块。
project/
package/
__init__.py
module.py
tests/
__init__.py
test_module.py
discover_tests.py
setup.py
Here's discover_tests.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)
And here's setup.py:
这是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)
回答by saschpe
If you use py27+ or py32+, the solution is pretty simple:
如果您使用 py27+ 或 py32+,解决方案非常简单:
test_suite="tests",
回答by antimatter
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.pyto module_test.py(basically add _testas a suffix to tests for a particular module), and python will find it automatically. Just make sure to add this to setup.py:
将你的重命名test_module.py为module_test.py(基本上_test作为后缀添加到特定模块的测试),python 会自动找到它。只需确保将此添加到setup.py:
from setuptools import setup, find_packages
setup(
...
test_suite = 'tests',
...
)
The long way
漫漫长路
Here's how to do it with your current directory structure:
以下是使用当前目录结构执行此操作的方法:
project/
package/
__init__.py
module.py
tests/
__init__.py
test_module.py
run_tests.py <- I want to delete this
setup.py
Under tests/__init__.py, you want to import the unittestand your unit test script test_module, and then create a function to run the tests. In tests/__init__.py, type in something like this:
在 下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
The TestLoaderclass has other functions besides loadTestsFromModule. You can run dir(unittest.TestLoader)to see the other ones, but this one is the simplest to use.
该TestLoader班除了具有其它功能loadTestsFromModule。您可以运行dir(unittest.TestLoader)查看其他的,但这个是最简单的使用。
Since your directory structure is such, you'll probably want the test_moduleto be able to import your modulescript. You might have already done this, but just in case you didn't, you could include the parent path so that you can import the packagemodule and the modulescript. At the top of your test_module.py, type:
由于您的目录结构是这样,您可能希望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
...
Then finally, in setup.py, include the testsmodule and run the command you created, my_module_suite:
最后,在 中setup.py,包含tests模块并运行您创建的命令my_module_suite:
from setuptools import setup, find_packages
setup(
...
test_suite = 'tests.my_module_suite',
...
)
Then you just run python setup.py test.
然后你就跑python setup.py test。
Here is a samplesomeone made as a reference.
这是某人制作的示例作为参考。
回答by CryingCyclops
Python's standard library unittestmodule 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 the discovercommand line argument to the unittestcommand.
Python 的标准库unittest模块支持发现(在 Python 2.7 及更高版本以及 Python 3.2 及更高版本中)。如果您可以假设这些最低版本,那么您只需将discover命令行参数添加到unittest命令中即可。
Only a small tweak is needed to setup.py:
只需要一个小的调整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,
},
)
回答by jwelsh
This won't remove run_tests.py, but will make it work with setuptools. Add:
这不会删除 run_tests.py,但会使其与 setuptools 一起使用。添加:
class Loader(unittest.TestLoader):
def loadTestsFromNames(self, names, _=None):
return self.discover(names[0])
Then in setup.py: (I assume you're doing something like setup(**config))
然后在 setup.py 中:(我假设你正在做类似的事情setup(**config))
config = {
...
'test_loader': 'run_tests:Loader',
'test_suite': '.', # your start_dir for discover()
}
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.
我看到的唯一缺点是它扭曲了 的语义loadTestsFromNames,但 setuptools 测试命令是唯一的使用者,并以指定的方式调用它。
回答by Michael Gruenstaeudl
From Building and Distributing Packages with Setuptools(emphasis mine):
从使用 Setuptools 构建和分发包(重点是我的):
test_suite
A string naming a unittest.TestCase subclass (or a package or module containing one or more of them, or a method of such a subclass), or naming a function that can be called with no arguments and returns a unittest.TestSuite.
测试套件
命名 unittest.TestCase 子类(或包含其中一个或多个子类的包或模块,或此类子类的方法)的字符串,或命名 可以不带参数调用并返回 unittest.TestSuite 的函数。
Hence, in setup.pyyou would add a function that returns a 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
Then, you would specify the command setupas follows:
然后,您可以setup按如下方式指定命令:
setup(
...
test_suite='setup.my_test_suite',
...
)

