Python単体テスト:テストファイルの一部だけを実行するにはどうすればよいですか?
-
21-08-2019 - |
質問
かなりの時間のかかるテスト (計算をクラスターに送信し、結果を待つテスト) を含むテスト ファイルがあります。これらはすべて、特定の TestCase クラス内にあります。
これらは時間がかかり、さらに中断する可能性が低いため、このテストのサブセットを実行するか実行しないかを選択できるようにしたいと考えています (最良の方法は、コマンドライン引数を使用することです。./tests.py --offline
" など)、ほとんどのテストを頻繁かつ迅速に実行し、時間があるときにセット全体を時々実行することができました。
今のところ、私はただ使っています unittest.main()
テストを開始します。
ありがとう。
解決
デフォルト unittest.main()
デフォルトのテストローダーを使用して、main が実行されているモジュールから TestSuite を作成します。
このデフォルトの動作を使用する必要はありません。
たとえば、3つ作ることができます ユニットテスト.TestSuite インスタンス。
「高速」サブセット。
fast = TestSuite() fast.addTests( TestFastThis ) fast.addTests( TestFastThat )
「遅い」サブセット。
slow = TestSuite() slow.addTests( TestSlowAnother ) slow.addTests( TestSlowSomeMore )
「全体」セット。
alltests = unittest.TestSuite([fast, slow])
Fast と TestCase を示すために TestCase 名を調整したことに注意してください。遅い。Subclass Unittest.testloaderは、クラスの名前を解析し、複数のローダーを作成できます。
その後、メインプログラムは次のようにコマンドライン引数を解析できます。 オプトパース または 引数解析 (2.7 または 3.2 以降で利用可能) 高速、低速、またはすべてを実行するスイートを選択します。
あるいは、それを信頼してもいいでしょう sys.argv[1]
は 3 つの値のいずれかであり、これと同じくらい単純なものを使用します
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
他のヒント
実際には、テスト ケースの名前を sys.argv として渡すことができ、それらのケースのみがテストされます。
たとえば、
class TestAccount(unittest.TestCase):
...
class TestCustomer(unittest.TestCase):
...
class TestShipping(unittest.TestCase):
...
account = TestAccount
customer = TestCustomer
shipping = TestShipping
電話をかけることができます
python test.py account
アカウントテストのみを行うことも、さらには
$ python test.py account customer
両方のケースをテストしてもらう
私は単純な方法を使用してこれを行っています skipIf
:
import os
SLOW_TESTS = int(os.getenv('SLOW_TESTS', '0'))
@unittest.skipIf(not SLOW_TESTS, "slow")
class CheckMyFeature(unittest.TestCase):
def runTest(self):
…
この方法では、既存のテスト ケースをこの 1 行で装飾するだけで済みます (テスト スイートなどを作成する必要はなく、その 1 つだけです) os.getenv()
単体テスト ファイルの先頭にある呼び出し行)、デフォルトとして、このテストはスキップされます。
遅いにもかかわらず実行したい場合は、次のようにスクリプトを呼び出すだけです。
SLOW_TESTS=1 python -m unittest …
基本的に次の 2 つの方法があります。
- クラス用に独自のテスト スイートを定義する
- 実際のデータを返すクラスター接続のモック クラスを作成します。
私は彼の 2 番目のアプローチを強く支持します。単体テスト すべき 複雑なシステム (データベースやクラスターなど) ではなく、コードのまさに単位のみをテストします。しかし、それが常に可能であるとは限らないことは理解しています。場合によっては、モックアップの作成にコストがかかりすぎたり、テストの目標が実際には複雑なシステムにある場合もあります。
オプション (1) に戻り、次の方法で続行できます。
suite = unittest.TestSuite()
suite.addTest(MyUnitTestClass('quickRunningTest'))
suite.addTest(MyUnitTestClass('otherTest'))
そしてスイートをテストランナーに渡します。
unittest.TextTestRunner().run(suite)
Python のドキュメントに関する詳細情報: http://docs.python.org/library/unittest.html#testsuite-objects
使っているので、 unittest.main()
ただ走ることができます python tests.py --help
ドキュメントを入手するには:
Usage: tests.py [options] [test] [...]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-f, --failfast Stop on first failure
-c, --catch Catch control-C and display results
-b, --buffer Buffer stdout and stderr during test runs
Examples:
tests.py - run default set of tests
tests.py MyTestSuite - run suite 'MyTestSuite'
tests.py MyTestCase.testSomething - run MyTestCase.testSomething
tests.py MyTestCase - run all 'test*' test methods
in MyTestCase
つまり、単純に行うことができます
python tests.py TestClass.test_method
または、 unittest.SkipTest()
関数。例: を追加します。 skipOrRunTest
次のようにテストクラスにメソッドを追加します。
def skipOrRunTest(self,testType):
#testsToRun = 'ALL'
#testsToRun = 'testType1, testType2, testType3, testType4,...etc'
#testsToRun = 'testType1'
#testsToRun = 'testType2'
#testsToRun = 'testType3'
testsToRun = 'testType4'
if ((testsToRun == 'ALL') or (testType in testsToRun)):
return True
else:
print "SKIPPED TEST because:\n\t testSuite '" + testType + "' NOT IN testsToRun['" + testsToRun + "']"
self.skipTest("skipppy!!!")
次に、この SkipOrRunTest メソッドへの呼び出しを、次のように各単体テストの先頭に追加します。
def testType4(self):
self.skipOrRunTest('testType4')
私は別の解決策を見つけました。 unittest.skip
デコレーター作品。を設定することで、 __unittest_skip__
そして __unittest_skip_why__
.
ラベルベース
ラベル付けシステムを適用して、いくつかのテストに次のようなラベルを付けたいと考えていました。 quick
, slow
, glacier
, memoryhog
, cpuhog
, core
, 、 等々。
それから実行します all 'quick' tests
, 、 または run everything except 'memoryhog' tests
, 、基本的なホワイトリスト/ブラックリスト設定
実装
これを 2 つの部分に分けて実装しました。
- まず、ラベルをテストに追加します (カスタム
@testlabel
クラスデコレータ) - カスタム
unittest.TestRunner
スキップするテストを特定し、実行前にテストリストの内容を変更します。
実用的な実装は次の要点にあります。https://gist.github.com/fragmuffin/a245f59bdcd457936c3b51aa2ebb3f6c
(完全に動作する例は長すぎてここに載せられません)
結果は…
$ ./runtests.py --blacklist foo
test_foo (test_things.MyTest2) ... ok
test_bar (test_things.MyTest3) ... ok
test_one (test_things.MyTests1) ... skipped 'label exclusion'
test_two (test_things.MyTests1) ... skipped 'label exclusion'
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK (skipped=2)
全て MyTests1
クラステストはスキップされます。 foo
ラベル。
--whitelist
も機能します
py.test、nose、場合によっては zope.testing などの専用のテストランナーの使用を検討してください。これらにはすべて、テストを選択するためのコマンド ライン オプションがあります。
たとえば、Nose を見てください。 https://pypi.python.org/pypi/nose/1.3.0
@slottの答えを試してみました:
if __name__ == "__main__":
suite = eval(sys.argv[1]) # Be careful with this line!
unittest.TextTestRunner().run(suite)
しかし、それにより次のエラーが発生しました。
Traceback (most recent call last):
File "functional_tests.py", line 178, in <module>
unittest.TextTestRunner().run(suite)
File "/usr/lib/python2.7/unittest/runner.py", line 151, in run
test(result)
File "/usr/lib/python2.7/unittest/case.py", line 188, in __init__
testMethod = getattr(self, methodName)
TypeError: getattr(): attribute name must be string
以下は私にとってうまくいきました:
if __name__ == "__main__":
test_class = eval(sys.argv[1])
suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
unittest.TextTestRunner().run(suite)
属性を追加することで、実行したいだけの test_* メソッドを選択する別の方法を見つけました。基本的に、メタクラスを使用して、StepDebug 属性を持つ TestCase クラス内の呼び出し可能オブジェクトを、unittest.skip デコレータで装飾します。詳細については、
デコレータとメタクラスを使用して、Python で 1 つを除くすべての単体テストをスキップする
上記の解決策よりも優れているかどうかはわかりませんが、オプションとして提供しているだけです。
これまでこれを行う良い方法が見つからなかったので、ここで共有します。
ゴール:テストファイルのセットを一緒に取得して、ユニットとして実行できるようにしますが、いずれかを選択して自分で実行することができます。
問題:Discover メソッドでは、実行する単一のテスト ケースを簡単に選択できません。
デザイン:以下を参照してください。これ 平らにする 名前空間なので、TestCase クラス名で選択でき、「tests1.test_core」プレフィックスは省略します。
./run-tests TestCore.test_fmap
コード
test_module_names = [
'tests1.test_core',
'tests2.test_other',
'tests3.test_foo',
]
loader = unittest.defaultTestLoader
if args:
alltests = unittest.TestSuite()
for a in args:
for m in test_module_names:
try:
alltests.addTest( loader.loadTestsFromName( m+'.'+a ) )
except AttributeError as e:
continue
else:
alltests = loader.loadTestsFromNames( test_module_names )
runner = unittest.TextTestRunner( verbosity = opt.verbose )
runner.run( alltests )
これが私にとって唯一うまくいったことです。
if __name__ == '__main__':
unittest.main( argv=sys.argv, testRunner = unittest.TextTestRunner(verbosity=2))
ただし、それを呼び出すときは、クラス名とテスト名を渡す必要がありました。クラスとテスト名の組み合わせを覚えていないので少し不便です。
python ./tests.py クラス名.test_30311
クラス名とテスト名を削除すると、ファイル内のすべてのテストが実行されます。CLI でコマンドを実際に変更しないため、組み込みメソッドよりもこの方がはるかに簡単に対処できることがわかりました。パラメータを追加するだけです。
お楽しみください、キース
テストを遅いテストとしてマークし、環境変数を使用してスキップできるデコレータを作成しました。
from unittest import skip
import os
def slow_test(func):
return skipIf('SKIP_SLOW_TESTS' in os.environ, 'Skipping slow test')(func)
次のように、テストを遅いとマークできるようになりました。
@slow_test
def test_my_funky_thing():
perform_test()
を設定することで遅いテストをスキップします。 SKIP_SLOW_TESTS
環境変数:
SKIP_SLOW_TESTS=1 python -m unittest