22

実行するunittest.main()と、Pythonはどのサブクラスunittest.Testcaseが存在するかをどのように知るのでしょうか。

たとえば、クラスを追加した場合、これを実行するFromRomanBadInput(unittest.TestCase)方法をどのようにunittest知ることができますか?

4

3 に答える 3

31

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ます。これは、ネストされたクラスを拾わないようにするためだと思います。)

于 2012-04-11T05:07:22.057 に答える
7

'main'関数は、インポートされたモジュールでunittest.TestCaseを継承するすべてのクラスを検索します。そして現在のパス、そして'test'で始まる各メソッドを実行しようとします

Pythonのドキュメントから:

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という文字で始まるメソッドで定義されます。この命名規則は、どのメソッドがテストを表すかについてテストランナーに通知します。

于 2012-04-11T04:29:03.620 に答える
1

以下の 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
于 2016-07-13T13:18:45.227 に答える