如何从“python setup.py test”运行unittest发现?

时间:2013-06-08 15:27:12

标签: python unit-testing nose pytest unittest2

我正在试图找出如何让python setup.py test运行等效的python -m unittest discover。我不想使用run_tests.py脚本,我不想使用任何外部测试工具(如nosepy.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)

7 个答案:

答案 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)

一种可能的解决方案是简单地扩展testdistutils / setuptools的{​​{1}}命令。这似乎是一个比我想要的更复杂的方法,但似乎在运行distribute时正确地发现并运行了我的包中的所有测试。我暂时选择这个作为我的问题的答案,希望有人能提供更优雅的解决方案:)

(灵感来自https://docs.pytest.org/en/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner

示例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中调用它。