如何正确对unittest TestCases进行排序?

时间:2016-03-11 01:48:59

标签: unit-testing python-3.x testing python-unittest

例如,我希望这些按照它们在文件中出现的顺序运行。

import unittest

class Test_MyTests(unittest.TestCase):

    def test_run_me_first(self): pass
    def test_2nd_run_me(self):   pass
    def test_and_me_last(self):  pass

class Test_AnotherClass(unittest.TestCase):

    def test_first(self):        pass
    def test_after_first(self):  pass
    def test_de_last_ding(self): pass

if __name__ == "__main__":
    unittest.main(verbosity=2)

运行此功能

test_after_first (__main__.Test_AnotherClass) ... ok
test_de_last_ding (__main__.Test_AnotherClass) ... ok
test_first (__main__.Test_AnotherClass) ... ok
test_2nd_run_me (__main__.Test_MyTests) ... ok
test_and_me_last (__main__.Test_MyTests) ... ok
test_run_me_first (__main__.Test_MyTests) ... ok

不是我想要的。

我搜索到的所有答案都是在搜索所有人之后跳舞并通过调用/分组测试或说

来避免问题
  • “不要这样做,只是以不同方式编写测试”或

  • “按字典顺序命名测试!”

... one除外,它可以满足我的需求:

loader = unittest.TestLoader()
ln = lambda f: getattr(MyTestCase, f).im_func.func_code.co_firstlineno
lncmp = lambda a, b: cmp(ln(a), ln(b))
loader.sortTestMethodsUsing = lncmp
unittest.main(testLoader=loader, verbosity=2)

但仅适用于一个TestCase类,仅适用于Python 2.

我想运行我所有的Python 3 unittest.TestCase子类,我希望能够指定用于排序的确切算法。

我可以在unittest中执行此操作吗?

1 个答案:

答案 0 :(得分:2)

经过批次的研究,得到了SO和Python的help的极大帮助,而且单元测试的文档完全没有,我得到了答案 I 最初想要,所以我想我会写这个来帮助别人,因为这是一个公平的(显然是常见的)请求。

要运行特定的测试用例,我们需要为该TestCase创建一个新的TestSuiteLet's do that适用于任意数量的TestCases:

def suiteFactory(*testcases):

    ln    = lambda f: getattr(tc, f).__code__.co_firstlineno
    lncmp = lambda a, b: ln(a) - ln(b)

    test_suite = unittest.TestSuite()
    for tc in testcases:
        test_suite.addTest(unittest.makeSuite(tc, sortUsing=lncmp))

    return test_suite

这很简单:

  1. 定义一个函数来获取函数的行号。 在Python 3中,我们之后的属性已从func.im_func.func_code.co_firstlineno更改为func.__code__.co_firstlineno,您可以使用dir(anyFunction)查看。

  2. 定义一个函数,根据cmp的行号对两个参数进行排序。由于人们可以做数学,cmp不在Python 3中,所以为了便于阅读,我刚刚完成了它的工作。

  3. 创建一个new空白TestSuite(),然后给它一个TestCase或者十个,然后告诉它使用#2号点排除TestCase的方法:cmp他们的行号

  4. 现在我们需要对文件的TestCase子类进行排序。

    为此,我们可以查看globals()及其属性。

    def caseFactory():
    
        from inspect import findsource
    
        g = globals().copy()
    
        cases = [
            g[obj] for obj in g
                if obj.startswith("Test")
                and issubclass(g[obj], unittest.TestCase)
        ]
    
        ordered_cases = sorted(cases, key=lambda f: findsource(f)[1])
    
        return ordered_cases
    

    这将只获取以unittest.TestCase开头的Test的所有子类,或您喜欢的任何命名约定,然后按行号对它们进行排序:findsource(object)返回源代码,和行号作为索引1,这是我们关心的。

    将它包装成我们可以使用的东西:

    if __name__ == "__main__":
        cases = suiteFactory(*caseFactory())
        runner = unittest.TextTestRunner(verbosity=2)
        runner.run(cases)
    

    这个 分隔输出,但如果最低级别的测试位于文件的顶部(或底部或某处),则可能是一件好事,并且应该运行之前更高级别的测试。

    那么完整输出就是:

    test_run_me_first (__main__.Test_MyTests) ... ok
    test_2nd_run_me (__main__.Test_MyTests) ... ok
    test_and_me_last (__main__.Test_MyTests) ... ok
    test_first (__main__.Test_AnotherClass) ... ok
    test_after_first (__main__.Test_AnotherClass) ... ok
    test_de_last_ding (__main__.Test_AnotherClass) ... ok
    
    ----------------------------------------------------------------------
    Ran 6 tests in 0.000s
    
    OK
    

    成功!

    您可以在Github gist.

    上找到更有趣的版本