22

我想知道当我们运行时unittest.main(),Python 怎么知道子类unittest.Testcase有什么?

例如,如果我添加一个类FromRomanBadInput(unittest.TestCase),如何unittest知道运行它?

4

3 回答 3

31

Python27/Lib所以我在我的目录中四处寻找......

unittest.main实际上是一个类的别名,unittest.TestProgram. 所以发生的事情是你构建了一个 this 的实例,并__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 的类。和当前路径,然后尝试运行以“测试”开头的每个方法

来自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 来创建的。这三个单独的测试是用名称以字母 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 回答