Python单元测试内存消耗

Python unittest memory consumption

在使用 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 变量。

现在测试通过的时候,没什么大不了的,但是测试失败的时候,list的内存好像没有释放。这会导致巨大的内存消耗,因为每个测试似乎都保留了它的内存。最终,每次测试运行并打印结果后,释放内存,一切恢复正常。

虽然上面的代码有些夸张,但真实案例包含 200 多个测试,每个测试使用大约 20-30 MB 的内存。如果这些没有被释放,我将 运行 陷入内存短缺。

似乎 unittest 保留测试方法变量以在测试失败时报告值,或者至少在这种情况下提供对变量的报告。我不知道,也许我在这里忽略了一些东西。

但是,我需要摆脱这些多余的内存。到目前为止,我的选择是:

我很想听听设置某种标志的可能性。我更希望听到有人指出我犯的一个明显错误,或者有人告诉我,使用 python 或 unittest 的 x.y 版本不会发生这种情况。

使用的版本:python3.3.5 final 64bit

所以,如果还有其他问题,我很乐意回答。如果您有任何想法,或者在黑暗中拍摄,请告诉我,我会尝试一下。

提前致谢。

问题可能是测试运行程序(或结果 class)保留了抛出的异常,该异常包含对引用大对象的帧的引用。您可能想要做的是编写一个不显示此行为的自定义运行程序。类似的东西(对 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)

虽然这里可能还有一些改进的余地。例如,您 可以 递归地检查回溯并确定它是否足够大以促使它被替换(或者甚至可能从框架中删除有问题的对象)。对于被测代码 raises 异常的情况尤其如此(在这种情况下,您可能会对回溯感兴趣,而不仅仅是占位符回溯)。

另一种解决方案可能是不在同一堆栈帧中进行分配,因为从失败测试创建的堆栈帧仅包含其上的帧。喜欢:

def mem1(self):
        list1 = [9876543210] * 2048*2048*9
        time.sleep(1)


def test_unittest_mem1(self):
        self.mem1()

        self.assertTrue(self.__result, "Failed")