Python単体テストを含むディレクトリがあります。各ユニットテストモジュールの形式は、test _ *。pyです。 all_test.pyというファイルを作成しようとしていますが、それは、あなたが推測したように、前述のテストフォームのすべてのファイルを実行し、結果を返します。これまでに2つの方法を試しました。両方とも失敗しました。 2つの方法を示しますが、実際にこれを正しく行う方法を知っている人がいることを願っています。
私の最初の勇敢な試みとして、「ファイル内のすべてのテストモジュールをインポートし、それからこのunittest.main()
doodadを呼び出すと、動作するでしょうか?」と考えました。まあ、私は間違っていたことがわかります。
import glob
import unittest
testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]
if __== "__main__":
unittest.main()
これは機能しませんでした、私が得た結果は:
$ python all_test.py
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
しかし、2回目の試行では、このテスト全体をより「手動」の方法で実行しようとするかもしれません。だから私はそれを以下にしようとしました:
import glob
import unittest
testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]
[__import__(str) for str in module_strings]
suites = [unittest.TestLoader().loadTestsFromName(str) for str in module_strings]
[testSuite.addTest(suite) for suite in suites]
print testSuite
result = unittest.TestResult()
testSuite.run(result)
print result
#Ok, at this point I have a result
#How do I display it as the normal unit test command line output?
if __== "__main__":
unittest.main()
これも機能しませんでしたが、とても近いようです!
$ python all_test.py
<unittest.TestSuite tests=[<unittest.TestSuite tests=[<unittest.TestSuite tests=[<test_main.TestMain testMethod=test_respondes_to_get>]>]>]>
<unittest.TestResult run=1 errors=0 failures=0>
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
ある種のスイートがあるようで、結果を実行できます。私はrun=1
しか持っていないということを少し心配していますが、それはrun=2
のように思えますが、進行中です。しかし、どのように結果をメインに渡し、表示するのですか?または、このファイルを実行するために基本的にどのように動作させるのですか?そうすることで、このディレクトリ内のすべての単体テストを実行しますか?
これを行うテストランナーを使用できます。 nose は非常に良い例です。実行すると、現在のツリーでテストを見つけて実行します。
更新しました:
ここに私の鼻の前の日からのいくつかのコードがあります。おそらく、モジュール名の明示的なリストは必要ないでしょうが、おそらく残りは役に立つでしょう。
testmodules = [
'cogapp.test_makefiles',
'cogapp.test_whiteutils',
'cogapp.test_cogapp',
]
suite = unittest.TestSuite()
for t in testmodules:
try:
# If the module defines a suite() function, call it to get the suite.
mod = __import__(t, globals(), locals(), ['suite'])
suitefn = getattr(mod, 'suite')
suite.addTest(suitefn())
except (ImportError, AttributeError):
# else, just load all the test cases from the module.
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))
unittest.TextTestRunner().run(suite)
Python 2.7以降では、新しいコードを記述したり、サードパーティのツールを使用してこれを行う必要はありません。コマンドラインを介した再帰的なテストの実行が組み込まれています。
python -m unittest discover <test_directory>
# or
python -m unittest discover -s <directory> -p '*_test.py'
python 2.7 または python 3.x unittestのドキュメントで詳細を読むことができます。
これは、unittestから直接可能になりました: nittest.TestLoader.discover 。
import unittest
loader = unittest.TestLoader()
start_dir = 'path/to/your/test/files'
suite = loader.discover(start_dir)
runner = unittest.TextTestRunner()
runner.run(suite)
python 3では、unittest.TestCase
を使用している場合:
test
ディレクトリに空の(またはそれ以外の)__init__.py
ファイルが必要です(must be named named test/
)test/
内のテストファイルは、パターンtest_*.py
と一致します。これらはtest/
の下のサブディレクトリ内に配置でき、これらのサブディレクトリには任意の名前を付けることができます。次に、以下を使用してすべてのテストを実行できます。
python -m unittest
できた! 100行未満のソリューション。別のpython初心者がこれを見つけることで時間を節約できれば幸いです。
さて、上記のコードを少し調べて(具体的にはTextTestRunner
とdefaultTestLoader
を使用して)、かなり接近することができました。最終的には、「手動」で追加するのではなく、すべてのテストスイートを単一のスイートコンストラクターに渡すことでコードを修正し、他の問題を修正しました。だからここに私の解決策があります。
import glob
import unittest
test_files = glob.glob('test_*.py')
module_strings = [test_file[0:len(test_file)-3] for test_file in test_files]
suites = [unittest.defaultTestLoader.loadTestsFromName(test_file) for test_file in module_strings]
test_suite = unittest.TestSuite(suites)
test_runner = unittest.TextTestRunner().run(test_suite)
ええ、おそらくこれをするよりも鼻を使う方が簡単かもしれませんが、それはポイントのほかです。
さまざまなテストケースクラスからすべてのテストを実行したい場合、それらを明示的に指定したい場合は、次のようにできます。
from unittest import TestLoader, TextTestRunner, TestSuite
from uclid.test.test_symbols import TestSymbols
from uclid.test.test_patterns import TestPatterns
if __== "__main__":
loader = TestLoader()
tests = [
loader.loadTestsFromTestCase(test)
for test in (TestSymbols, TestPatterns)
]
suite = TestSuite(tests)
runner = TextTestRunner(verbosity=2)
runner.run(suite)
ここで、uclid
は私のプロジェクトであり、TestSymbols
およびTestPatterns
はTestCase
のサブクラスです。
discover
メソッドとload_tests
のオーバーロードを使用して、コードの(最小限の、私は)行数でこの結果を達成しました。
def load_tests(loader, tests, pattern):
''' Discover and load all unit tests in all files named ``*_test.py`` in ``./src/``
'''
suite = TestSuite()
for all_test_suite in unittest.defaultTestLoader.discover('src', pattern='*_tests.py'):
for test_suite in all_test_suite:
suite.addTests(test_suite)
return suite
if __== '__main__':
unittest.main()
5のようなもののようなもの
Ran 27 tests in 0.187s
OK
さまざまなアプローチを試しましたが、すべてに欠陥があるように見えるか、いくつかのコードを作成する必要があり、それは面倒です。しかし、Linuxには便利な方法があります。特定のパターンですべてのテストを見つけて、それらを1つずつ呼び出します。
find . -name 'Test*py' -exec python '{}' \;
そして最も重要なことは、それは間違いなく動作します。
packagedライブラリまたはアプリケーションの場合、それを行いたくありません。 setuptools
あなたのためにそれを行います 。
このコマンドを使用するには、関数、TestCaseクラスまたはメソッド、または
unittest
クラスを含むモジュールまたはパッケージのいずれかによって、プロジェクトのテストをTestCase
テストスイートにラップする必要があります。名前付きスイートがモジュールであり、モジュールにadditional_tests()
関数がある場合、そのモジュールが呼び出され、実行されるテストに結果(unittest.TestSuite
でなければなりません)が追加されます。名前付きスイートがパッケージの場合、すべてのサブモジュールとサブパッケージがテストスイート全体に再帰的に追加されます。
次のように、ルートテストパッケージの場所を指定するだけです。
setup(
# ...
test_suite = 'somepkg.test'
)
そして、python setup.py test
を実行します。
discover
はファイルインポートを使用するため、テストスイートでの相対的なインポートを回避しない限り、Python 3ではファイルベースの検出に問題がある可能性があります。オプションのtop_level_dir
をサポートしていますが、無限の再帰エラーがいくつかありました。したがって、パッケージ化されていないコードの簡単な解決策は、テストパッケージの__init__.py
に以下を追加することです( load_tests Protocol を参照)。
import unittest
from . import foo, bar
def load_tests(loader, tests, pattern):
suite = unittest.TestSuite()
suite.addTests(loader.loadTestsFromModule(foo))
suite.addTests(loader.loadTestsFromModule(bar))
return suite
私はPyDev/LiClipseを使用していますが、GUIからすべてのテストを一度に実行する方法を実際に理解していません。 (編集:ルートテストフォルダーを右クリックし、Run as -> Python unit-test
を選択します
これは私の現在の回避策です:
import unittest
def load_tests(loader, tests, pattern):
return loader.discover('.')
if __== '__main__':
unittest.main()
このコードをテストディレクトリのall
というモジュールに配置しました。このモジュールをLiClipseから単体テストとして実行すると、すべてのテストが実行されます。特定のテストまたは失敗したテストのみを繰り返すように要求した場合、それらのテストのみが実行されます。コマンドラインのテストランナーにも干渉しません(nosetests)-無視されます。
プロジェクトのセットアップに基づいて、引数をdiscover
に変更する必要がある場合があります。
Stephen Cagle の回答に基づいて、ネストされたテストモジュールのサポートを追加しました。
import fnmatch
import os
import unittest
def all_test_modules(root_dir, pattern):
test_file_names = all_files_in(root_dir, pattern)
return [path_to_module(str) for str in test_file_names]
def all_files_in(root_dir, pattern):
matches = []
for root, dirnames, filenames in os.walk(root_dir):
for filename in fnmatch.filter(filenames, pattern):
matches.append(os.path.join(root, filename))
return matches
def path_to_module(py_file):
return strip_leading_dots( \
replace_slash_by_dot( \
strip_extension(py_file)))
def strip_extension(py_file):
return py_file[0:len(py_file) - len('.py')]
def replace_slash_by_dot(str):
return str.replace('\\', '.').replace('/', '.')
def strip_leading_dots(str):
while str.startswith('.'):
str = str[1:len(str)]
return str
module_names = all_test_modules('.', '*Tests.py')
suites = [unittest.defaultTestLoader.loadTestsFromName(mname) for mname
in module_names]
testSuite = unittest.TestSuite(suites)
runner = unittest.TextTestRunner(verbosity=1)
runner.run(testSuite)
このコードは、.
のすべてのサブディレクトリで*Tests.py
ファイルを検索し、これらのファイルが読み込まれます。それは、各*Tests.py
が単一のクラス*Tests(unittest.TestCase)
を含むことを期待しています。このクラスは順番にロードされ、次々に実行されます。
これは、ディレクトリ/モジュールの任意の深いネストで機能しますが、その間の各ディレクトリには少なくとも空の__init__.py
ファイルが含まれている必要があります。これにより、テストは、スラッシュ(またはバックスラッシュ)をドット(replace_slash_by_dot
を参照)に置き換えることで、ネストされたモジュールをロードできます。
このBASHスクリプトは、現在の作業ディレクトリに関係なく、ファイルシステムのANYWHEREからpython unittestテストディレクトリを実行します。その作業ディレクトリは、常にtest
ディレクトリが置かれている場所にあります。
すべてのテスト、独立した$ PWD
unittest Pythonモジュールは、(discover -s
オプションを使用して)場所を指定しない限り、現在のディレクトリに依存します。
これは、./src
または./example
作業ディレクトリにとどまり、素早い全体的な単体テストが必要な場合に便利です。
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"
python -m unittest discover -s "$readlink"/test -v
選択されたテスト、独立した$ PWD
このユーティリティファイルにrunone.py
という名前を付け、次のように使用します。
runone.py <test-python-filename-minus-dot-py-fileextension>
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"
(cd "$dirname"/test; python -m unittest $1)
実動中にパッケージ/メモリのオーバーヘッドに負担をかけるtest/__init__.py
ファイルは必要ありません。
テスト発見は完全な主題であると思われるため、テスト発見のためのいくつかの専用フレームワークがあります:
詳細はこちら: https://wiki.python.org/moin/PythonTestingToolsTaxonomy