web-dev-qa-db-ja.com

「python setup.py test」からunittest discoverを実行する方法は?

python setup.py testを取得してpython -m unittest discoverに相当するものを実行する方法を見つけようとしています。 run_tests.pyスクリプトを使用したくないし、外部テストツール(nosepy.testなど)を使用したくない。ソリューションがpython 2.7でのみ動作する場合は問題ありません。

setup.pyで、configの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

Update:これは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のみを使用するソリューションに移行することでこれを解消できることを望んでいます。

削除したいrun_tests.pyは次のとおりです。

import unittest

if __== '__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)
70
cdwilson

Py27 +またはpy32 +を使用する場合、解決策は非常に簡単です。

test_suite="tests",
39
saschpe

Setuptoolsを使用したパッケージのビルドおよび配布 (私の強調):

test_suite

Unittest.TestCaseサブクラス(または1つ以上のサブクラスを含むパッケージまたはモジュール、またはそのようなサブクラスのメソッド)を命名する文字列、または引数なしで呼び出すことができる関数の命名 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',
    ...
)
34

これを機能させるために設定は必要ありません。基本的には、主に2つの方法があります。

簡単な方法

_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_を実行します。

サンプル 参照として作成された誰かです。

18
antimatter

可能な解決策の1つは、testおよびdistutils/setuptoolsdistributeコマンドを単純に拡張することです。これは私が好むよりも複雑で複雑なように思えますが、python setup.py test。誰かがよりエレガントなソリューションを提供してくれることを期待して、私の質問への回答としてこれを選択することを保留しています:)

https://docs.pytest.org/en/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner に触発されました)

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

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,
    },
)
3
CryingCyclops

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

これは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テストコマンドが唯一のコンシューマであり、 指定された方法 で呼び出します。

0
jwelsh