How to run unittest discover from “python setup.py test”?

前端 未结 7 2055
不知归路
不知归路 2020-12-12 19:39

I\'m trying to figure out how to get python setup.py test to run the equivalent of python -m unittest discover. I don\'t want to use a run_tests.p

相关标签:
7条回答
  • 2020-12-12 19:59

    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.py to module_test.py (basically add _test as a suffix to tests for a particular module), and python will find it automatically. Just make sure to add this to 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 unittest and your unit test script test_module, and then create a function to run the tests. In tests/__init__.py, type in something like this:

    import unittest
    import test_module
    
    def my_module_suite():
        loader = unittest.TestLoader()
        suite = loader.loadTestsFromModule(test_module)
        return suite
    

    The TestLoader class has other functions besides loadTestsFromModule. You can run dir(unittest.TestLoader) to see the other ones, but this one is the simplest to use.

    Since your directory structure is such, you'll probably want the test_module to be able to import your module script. 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 package module and the module script. At the top of your test_module.py, type:

    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 tests module and run the command you created, my_module_suite:

    from setuptools import setup, find_packages
    
    setup(
        ...
        test_suite = 'tests.my_module_suite',
        ...
    )
    

    Then you just run python setup.py test.

    Here is a sample someone made as a reference.

    0 讨论(0)
  • 2020-12-12 20:01

    Another less than ideal solution slightly inspired by http://hg.python.org/unittest2/file/2b6411b9a838/unittest2/collector.py

    Add a module that returns a TestSuite of discovered tests. Then configure setup to call that module.

    project/
      package/
        __init__.py
        module.py
      tests/
        __init__.py
        test_module.py
      discover_tests.py
      setup.py
    

    Here's 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:

    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)
    
    0 讨论(0)
  • 2020-12-12 20:07

    One possible solution is to simply extend the test command 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 :)

    (Inspired by https://docs.pytest.org/en/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner)

    Example 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)
    
    0 讨论(0)
  • 2020-12-12 20:09

    This won't remove run_tests.py, but will make it work with setuptools. Add:

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

    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.

    0 讨论(0)
  • 2020-12-12 20:11

    From Building and Distributing Packages with Setuptools (emphasis mine):

    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.

    Hence, in setup.py you would add a function that returns a 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 setup as follows:

    setup(
        ...
        test_suite='setup.my_test_suite',
        ...
    )
    
    0 讨论(0)
  • 2020-12-12 20:13

    If you use py27+ or py32+, the solution is pretty simple:

    test_suite="tests",
    
    0 讨论(0)
提交回复
热议问题