我想对运行一次的产品的多个版本进行测试。这是代码示例:
import unittest
suite = unittest.TestLoader().discover("./tests")
runner = unittest.TextTestRunner()
for build in [build1, build2]:
get_the_build(build)
runner.run(suite)
第一次迭代效果很好,但是在第二次迭代的开始出现了一个错误:
Traceback (most recent call last):
File "D:/Path/to/my/folder/run_tests.py", line 9, in <module>
runner.run(suite)
File "C:\Program Files (x86)\Python36-32\lib\unittest\runner.py", line 176, in run
test(result)
File "C:\Program Files (x86)\Python36-32\lib\unittest\suite.py", line 84, in __call__
return self.run(*args, **kwds)
File "C:\Program Files (x86)\Python36-32\lib\unittest\suite.py", line 122, in run
test(result)
TypeError: 'NoneType' object is not callable
发生了什么事? runner
发出什么结果?为什么会失败?有什么想法可以解决问题吗?
答案 0 :(得分:2)
好吧,好吧。我花了生命的最后一个小时来查看GitHub中unittest
的代码,可以在here中找到它们。我刚去过suite.py
(here)的代码,这是您遇到的错误文件之一。这是TestSuite.run
的实际代码:
def run(self, result, debug=False):
topLevel = False
if getattr(result, '_testRunEntered', False) is False:
result._testRunEntered = topLevel = True
for index, test in enumerate(self):
if result.shouldStop:
break
if _isnotsuite(test):
self._tearDownPreviousClass(test, result)
self._handleModuleFixture(test, result)
self._handleClassSetUp(test, result)
result._previousTestClass = test.__class__
if (getattr(test.__class__, '_classSetupFailed', False) or
getattr(result, '_moduleSetUpFailed', False)):
continue
if not debug:
test(result)
else:
test.debug()
if self._cleanup:
self._removeTestAtIndex(index)
if topLevel:
self._tearDownPreviousClass(None, result)
self._handleModuleTearDown(result)
result._testRunEntered = False
return result
因此,基本上,这段代码的作用是迭代套件所具有的每个测试并调用它:
for index, test in enumerate(self):
...
if not debug:
test(result) # This is the line throwing the error
...
如您所见,该循环在套件本身上进行迭代,因此我必须在 somewhere 中定义一个__iter__
方法。在类TestSuite
中找不到它5分钟后,我意识到是拥有此类方法的父类。这是我在BaseTestSuite
中找到的:
def __iter__(self):
return iter(self._tests)
基本上,它只是返回测试的 iterator 。在那一刻,这样的代码行是我无法确定的一堵高墙。但是我没有放弃,回到了TestSuite.run
的定义,并且奇迹般地,我发现了以下几行:
...
if self._cleanup:
self._removeTestAtIndex(index)
...
这让我感到奇怪:“测试被删除了吗?让我调查一下” 。然后我就很开明,因为在_removeTestAtIndex
里面发现了这一行:
self._tests[index] = None
故事的结尾。因此,在第一次运行所有测试后,它们只转换为None
:套件内的测试列表最终为None
s([None, None, ..., None]
的列表)。
因此,您如何防止这种行为?只需关闭套件中的_cleanup
标志即可。这应该起作用:
import unittest
suite = unittest.TestLoader().discover("./tests")
suite._cleanup = False # Pervent such cleanup
runner = unittest.TextTestRunner()
for build in [build1, build2]:
get_the_build(build)
runner.run(suite)
很抱歉,但是,除了向您展示如何解决问题外,我还想教您如何调试任何东西。
让我知道这是否真的对您有用。否则,请告诉我出了什么问题。
答案 1 :(得分:0)
感觉就像一个可怕的黑客,但每次在运行之前制作套件的深层副本为我解决了问题:
import unittest
import copy
suite = unittest.TestLoader().discover("./tests")
runner = unittest.TextTestRunner()
for build in [build1, build2]:
get_the_build(build)
runner.run(copy.deepcopy(suite))