在使用python的unittest框架时,我注意到在我的情况下会导致一些问题的行为。要演示它,请查看以下代码:
import unittest
import time
class TC_Memory(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
self.__result = False
def test_unittest_mem1(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
def test_unittest_mem2(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
def test_unittest_mem3(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
def test_unittest_mem4(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
def test_unittest_mem5(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
def test_unittest_mem6(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
def test_unittest_mem7(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
def test_unittest_mem8(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
def test_unittest_mem9(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
self.assertTrue(self.__result, "Failed")
if __name__ == "__main__":
unittest.main()
这些测试方法完成所有相同的操作。生成一个巨大的列表,等待一秒,然后根据__result变量传递或失败。
现在,当测试通过时,没有什么大的事情发生,但是当测试失败时,列表的内存似乎不会被释放。这会导致巨大的内存消耗,因为每个测试似乎都能保留其内存。最后,在每次测试运行并打印结果后,内存被释放,一切都恢复正常。
虽然上面的代码夸大了,但真实案例包含200多个测试,每个测试使用大约20-30 MB的内存。如果那些没有被释放,我就会遇到内存不足。
如果测试失败,或者至少在这种情况下报告变量,似乎unittest保留测试方法变量用于报告值。我不知道,也许我在这里忽略了什么。
然而,我需要摆脱这种多余的记忆。到目前为止,我的选择是:
我很想听听设置某种旗帜的可能性。甚至更多我喜欢听到有人指出我做出的明显错误或有人告诉我它不会发生使用python或unittest的x.y版本。
至于使用的版本:它是python 3.3.5 final 64bit
所以,如果还有其他问题,我很乐意回答这些问题。如果你有任何想法,或在黑暗中拍摄,让我听一听,我会尝试一下。
提前致谢。
答案 0 :(得分:2)
问题可能是测试运行器(或结果类)保留了抛出的异常,该异常包含对引用大对象的帧的引用。您可能想要做的是编写一个不显示此行为的自定义运行器。喜欢的东西(抱歉python2,但这就是我现在所拥有的):
class CustomTestResult(TextTestResult):
def addError(self, test, err):
tp, vl, tb = err
super(CustomTestResult, self).addError(test, (tp, vl, placeholder))
def addFailure(self, test, err):
tp, vl, tb = err
super(CustomTestResult, self).addFailure(test, (tp, vl, placeholder))
class CustomTestRunner(TextTestRunner):
resultclass = CustamTestResult
if __name__ == "__main__":
import sys
try:
raise Exception
except Exception as err:
placeholder = sys.exc_info()[2]
unittest.main(testRunner = CustomTestRunner)
虽然这里可能有一些改进空间。你可以例如递归地检查回溯并确定它是否足够大以促使它被替换(甚至可能从框架中删除有问题的对象)。对于测试中的代码raise
的异常情况尤其如此(在这种情况下,您可能会对回溯感兴趣,而不仅仅是占位符回溯)。
另一种解决方案可能是不在同一堆栈帧中进行分配,因为从失败测试创建的堆栈帧将仅包含帧。像:
def mem1(self):
list1 = [9876543210] * 2048*2048*9
time.sleep(1)
def test_unittest_mem1(self):
self.mem1()
self.assertTrue(self.__result, "Failed")