Python unittest.py不';我不能和trace.py玩得很好-为什么?

Python unittest.py不';我不能和trace.py玩得很好-为什么?,python,unit-testing,code-coverage,python-unittest,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 fooba

哇。今晚我发现,使用
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
对其产生的任何影响。无需向您显示输出;它看起来就像上面的成功例子

因此,我有两个问题:

  • 这是怎么回事?为什么
    trace
    会把
    unittest
    搞砸

  • 有没有更简单和/或更不疯狂的方法来解决这个问题


  • 我不知道为什么
    trace
    不能正常工作,但是:


    一个更简单的解决方法是将模块的名称显式传递给
    unittest.main

    import unittest
    
    class Tester(unittest.TestCase):
        def test_true(self):
            self.assertTrue(True)
    
    if __name__ == "__main__":
        unittest.main(module='foobar')
    
    trace
    会在
    unittest
    中搞乱测试发现,因为
    trace
    是如何加载它正在运行的模块的<代码>跟踪读取模块源代码,编译它,并在
    \uuuuuu名称
    全局设置为
    '\uuuuu main'
    的上下文中执行它。这足以使大多数模块的行为看起来就像它们被调用为主模块一样,但实际上不会更改在Python解释器中注册为
    \uuuuu main\uuuuu
    的模块。当
    unittest
    要求
    \uuuuu main\uuuuu
    模块扫描测试用例时,它实际上会从命令行调用
    跟踪
    模块,该模块当然不包含单元测试


    coverage.py
    采取了不同的方法来实际替换
    sys.modules

    中名为
    \uuu main\uuu
    的模块,我喜欢Theran的答案,但至少在Python 3.6上有一些缺陷:

    如果我运行了
    foobar.py
    ,但如果我运行了
    foobar.py Sometestclass
    ,只执行
    Sometestclass
    ,trace不会选择它并运行所有测试

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

    请记住,unittest通常作为

    python foobar.py
    因此目标测试始终是最后一个参数,除非它是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)
    

    无论如何,现在trace只执行所需的测试,或者如果我没有另外指定,则执行所有测试。

    我喜欢coverage.py,但我切换到trace.py,因为我需要它生成的*.cover文件(用于CDash执行的自动覆盖率分析)。有没有办法让coverage.py发出这些文件?我不知道.cover文件中有什么,但要从coverage.py收集的数据中生成它们并不难。请通过电子邮件联系我们,以便我们可以讨论。非常好的解释,谢谢。我只需要告诉测试模块它自己的(文件)名是什么,它本身和trace.py下都像一个符咒一样工作。顺便说一句:尝试将cProfile和unittest与
    python-mcprofile[some_test_file.py]
    一起使用时,会遇到同样的问题,解决方案也是一样的:
    unittest.main(module='tests')
    。搜索引擎没有找到任何特定于cProfile的内容,所以添加了一条评论来帮助搜索。
        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)