実行するunittest.main()
と、Pythonはどのサブクラスunittest.Testcase
が存在するかをどのように知るのでしょうか。
たとえば、クラスを追加した場合、これを実行するFromRomanBadInput(unittest.TestCase)
方法をどのようにunittest
知ることができますか?
実行するunittest.main()
と、Pythonはどのサブクラスunittest.Testcase
が存在するかをどのように知るのでしょうか。
たとえば、クラスを追加した場合、これを実行するFromRomanBadInput(unittest.TestCase)
方法をどのようにunittest
知ることができますか?
Python27/Lib
だから私は自分のディレクトリを見回しました...
unittest.main
は、実際にはクラスのエイリアスですunittest.TestProgram
。それで何が起こるかというと、これのインスタンスとその__init__
実行を構築することです。これは、呼び出し元のモジュールの動的インポートを含む、一連の健全性チェックと構成を行います(モジュールの名前として__import__
関数を使用します__main__
デフォルトでインポートします)。これでself.module
、ソースを表すモジュール オブジェクトを含む属性が追加されました。
最終的に、次のコードに到達します。
self.test = self.testLoader.loadTestsFromModule(self.module)
のself.testLoader
インスタンスですunittest.TestLoader
。そのメソッドには、とりわけ以下が含まれます。
for name in dir(module):
obj = getattr(module, name)
if isinstance(obj, type) and issubclass(obj, case.TestCase):
tests.append(self.loadTestsFromTestCase(obj))
したがってdir
、モジュール オブジェクトの を使用して、定義したすべてのグローバル変数 (クラスを含む) の名前を取得し、それを派生元のクラスのみにフィルター処理しunittest.TestCase
(ローカルでcase.TestCase
は、そのエイリアスです)、内部のテスト メソッドを探します。tests
リストに追加するクラス。その検索は同様に動作します。
def isTestMethod(attrname, testCaseClass=testCaseClass,
prefix=self.testMethodPrefix):
return attrname.startswith(prefix) and \
hasattr(getattr(testCaseClass, attrname), '__call__')
testFnNames = filter(isTestMethod, dir(testCaseClass))
そのため、クラスの を使用して、dir
試す名前のリストを取得し、それらの名前の属性を探し、self.testMethodPrefix
('test'
デフォルトでは) で始まり、呼び出し可能な (属性を持つ__call__
) 属性を選択します。(実際、ここで組み込み関数を使用していないことに驚いていcallable
ます。これは、ネストされたクラスを拾わないようにするためだと思います。)
'main'関数は、インポートされたモジュールでunittest.TestCaseを継承するすべてのクラスを検索します。そして現在のパス、そして'test'で始まる各メソッドを実行しようとします
import random
import unittest
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
self.seq = range(10)
def test_shuffle(self):
# make sure the shuffled sequence does not lose any elements
random.shuffle(self.seq)
self.seq.sort()
self.assertEqual(self.seq, range(10))
# should raise an exception for an immutable sequence
self.assertRaises(TypeError, random.shuffle, (1,2,3))
def test_choice(self):
element = random.choice(self.seq)
self.assertTrue(element in self.seq)
def test_sample(self):
with self.assertRaises(ValueError):
random.sample(self.seq, 20)
for element in random.sample(self.seq, 5):
self.assertTrue(element in self.seq)
if __name__ == '__main__':
unittest.main()
テストケースは、unittest.TestCaseをサブクラス化することによって作成されます。3つの個別のテストは、名前がtestという文字で始まるメソッドで定義されます。この命名規則は、どのメソッドがテストを表すかについてテストランナーに通知します。
以下の unittest.main() と同様の動作を試みるコードをいくつか書きました。要約すると、モジュールを反復処理し、「unittest」という名前で始まらないモジュールについては、そのメンバーを検査します。次に、それらのメンバーがクラスであり、unittest.TestCase のサブクラスである場合、そのクラスのメンバーを解析します。次に、それらのクラスのメンバーが「test」で始まる関数またはメソッドである場合、それをテストのリストに追加します。クラスオブジェクトの__dict__
inspect.getmembers を使用すると多くのことが表示される可能性があるため、メソッド/関数をイントロスペクトするために使用されます。最後に、そのテストのリストがタプルに変換され、スイートとしてまとめられます。次に、詳細レベル 2 のランナーを使用してスイートが実行されます。もちろん、関数/メソッド名の先頭にある「test」をチェックする正規表現を削除すると、テストのリストに bar_test() を含めることができます。その制限を望まない場合。
#!/usr/bin/env python
import unittest
import inspect
import sys
import re
class Foo(unittest.TestCase):
@staticmethod
def test_baz():
pass
@classmethod
def test_mu(cls):
pass
def test_foo(self):
self.assertEqual('foo', 'foo')
def bar_test(self):
self.assertEqual('bar', 'bar')
class Bar:
pass
if __name__ == '__main__':
runner = unittest.TextTestRunner(verbosity=2)
tests = []
is_member_valid_test_class = lambda member: inspect.isclass(member) and \
issubclass(member, unittest.TestCase)
for module_name, module_obj in sys.modules.items():
if not re.match(r'unittest', module_name):
for cls_name, cls in inspect.getmembers(
module_obj, is_member_valid_test_class):
for methname, methobj in cls.__dict__.items():
if inspect.isroutine(methobj) and re.match(r'test', methname):
tests.append(cls(methname))
suite = unittest.TestSuite(tests=tuple(tests))
runner.run(suite)
結果の出力は次のとおりです。
test_foo (__main__.Foo) ... ok
test_baz (__main__.Foo) ... ok
test_mu (__main__.Foo) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK