unittest.py与trace.py不能很好地合作 - 为什么?

时间:2014-05-16 02:00:17

标签: python unit-testing code-coverage python-unittest

哇。今晚我发现使用unittest模块编写的Python单元测试不能很好地与trace模块下的覆盖率分析相匹配。这是foobar.py中最简单的单元测试:

import unittest

class Tester(unittest.TestCase):
    def test_true(self):
        self.assertTrue(True)

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

如果我使用python foobar.py运行此操作,我会收到此输出:

 .
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

大。现在我也想进行覆盖测试,所以我用python -m trace --count -C . foobar.py再次运行它,但现在我明白了:

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

不,Python,它不行 - 你没有进行我的测试!似乎在trace的上下文中以某种方式运行unittest的测试检测机制。这是我提出的(疯狂)解决方案:

import unittest

class Tester(unittest.TestCase):
    def test_true(self):
        self.assertTrue(True)

class Insane(object):
    pass

if __name__ == "__main__":
    module = Insane()
    for k, v in locals().items():
        setattr(module, k, v)

    unittest.main(module)

这基本上是一种解决方法,它通过伪造顶级模块的副本来提升顶层模块的抽象,不可命名的名称。然后,我可以将该名称传递给unittest.main(),以便回避trace对其产生的任何影响。无需向您显示输出;它看起来就像上面的成功例子。

所以,我有两个问题:

  1. 这里发生了什么?为什么trace搞砸了unittest

  2. 是否有更容易和/或更少的疯狂方法来解决这个问题?

3 个答案:

答案 0 :(得分:8)

更简单的解决方法是将模块名称明确传递给unittest.main

import unittest

class Tester(unittest.TestCase):
    def test_true(self):
        self.assertTrue(True)

if __name__ == "__main__":
    unittest.main(module='foobar')

traceunittest中混淆了测试发现,因为trace如何加载正在运行的模块。 trace读取模块源代码,编译它,并在__name__全局设置为'__main__'的上下文中执行它。这足以使大多数模块的行为就像它们被称为主模块一样,但实际上并没有改变在Python解释器中注册为__main__的模块。当unittest要求__main__模块扫描测试用例时,它实际上从命令行调用trace模块,当然这不包含单元测试。

coverage.py采用不同的方法实际替换__main__中名为sys.modules的模块。

答案 1 :(得分:3)

我不知道为什么trace无法正常工作,但coverage.py会这样做:

$ coverage run foobar.py
.
----------------------------------------------------------------------
Ran 1 test in 0.001s

OK
$ coverage report
Name     Stmts   Miss  Cover
----------------------------
foobar       6      0   100%

答案 2 :(得分:0)

我喜欢Theran的答案,但至少在Python 3.6上有一些问题:

如果我运行了foobar.py很好,但是如果我运行了foobar.py Sometestclass,只执行了Sometestclass,trace不会选择运行任何测试。

我的解决方法是在适当的时候指定defaultTest:

请记住,单元测试通常以

运行

python foobar.py <-flags and options> <TestClass.testmethod>,因此定向测试始终是最后一个参数,除非它是一个unittest选项,在这种情况下,它以-开头。还是foobar.py文件本身。

    lastarg = sys.argv[-1]
    #not a flag, not foobar.py either...
    if not lastarg.startswith("-") and not lastarg.endswith(".py"):
        defaultTest = lastarg
    else:
        defaultTest = None

    unittest.main(module=os.path.splitext(os.path.basename(__file__))[0], defaultTest=defaultTest)

无论如何,现在跟踪只执行所需的测试,如果没有另外指定,则全部执行。