python内存溢出_python - 获取Python的unittest会产生tearDown()方法 - 堆栈内存溢出...

灵感来自scoffey的答案 ,我决定采取mercilessnes到一个新的水平,并纷纷拿出以下。

它既可以在普通单元测试中使用,也可以在通过鼻子测试运行时使用,并且还可以在Python 2.7、3.2、3.3和3.4版本中使用(我没有专门测试3.0、3.1或3.5,因为我没有安装这些工具,此刻,但是如果我正确阅读了源代码 ,它也应该在3.5中工作):

#! /usr/bin/env python

from __future__ import unicode_literals

import logging

import os

import sys

import unittest

# Log file to see squawks during testing

formatter = logging.Formatter(fmt='%(levelname)-8s %(name)s: %(message)s')

log_file = os.path.splitext(os.path.abspath(__file__))[0] + '.log'

handler = logging.FileHandler(log_file)

handler.setFormatter(formatter)

logging.root.addHandler(handler)

logging.root.setLevel(logging.DEBUG)

log = logging.getLogger(__name__)

PY = tuple(sys.version_info)[:3]

class SmartTestCase(unittest.TestCase):

"""Knows its state (pass/fail/error) by the time its tearDown is called."""

def run(self, result):

# Store the result on the class so tearDown can behave appropriately

self.result = result.result if hasattr(result, 'result') else result

if PY >= (3, 4, 0):

self._feedErrorsToResultEarly = self._feedErrorsToResult

self._feedErrorsToResult = lambda *args, **kwargs: None # no-op

super(SmartTestCase, self).run(result)

@property

def errored(self):

if (3, 0, 0) <= PY < (3, 4, 0):

return bool(self._outcomeForDoCleanups.errors)

return self.id() in [case.id() for case, _ in self.result.errors]

@property

def failed(self):

if (3, 0, 0) <= PY < (3, 4, 0):

return bool(self._outcomeForDoCleanups.failures)

return self.id() in [case.id() for case, _ in self.result.failures]

@property

def passed(self):

return not (self.errored or self.failed)

def tearDown(self):

if PY >= (3, 4, 0):

self._feedErrorsToResultEarly(self.result, self._outcome.errors)

class TestClass(SmartTestCase):

def test_1(self):

self.assertTrue(True)

def test_2(self):

self.assertFalse(True)

def test_3(self):

self.assertFalse(False)

def test_4(self):

self.assertTrue(False)

def test_5(self):

self.assertHerp('Derp')

def tearDown(self):

super(TestClass, self).tearDown()

log.critical('---- RUNNING {} ... -----'.format(self.id()))

if self.errored:

log.critical('----- ERRORED -----')

elif self.failed:

log.critical('----- FAILED -----')

else:

log.critical('----- PASSED -----')

if __name__ == '__main__':

unittest.main()

使用unittest运行时:

$ ./test.py -v

test_1 (__main__.TestClass) ... ok

test_2 (__main__.TestClass) ... FAIL

test_3 (__main__.TestClass) ... ok

test_4 (__main__.TestClass) ... FAIL

test_5 (__main__.TestClass) ... ERROR

[…]

$ cat ./test.log

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----

CRITICAL __main__: ----- PASSED -----

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----

CRITICAL __main__: ----- FAILED -----

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----

CRITICAL __main__: ----- PASSED -----

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----

CRITICAL __main__: ----- FAILED -----

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----

CRITICAL __main__: ----- ERRORED -----

当进行nosetests :

$ nosetests ./test.py -v

test_1 (test.TestClass) ... ok

test_2 (test.TestClass) ... FAIL

test_3 (test.TestClass) ... ok

test_4 (test.TestClass) ... FAIL

test_5 (test.TestClass) ... ERROR

$ cat ./test.log

CRITICAL test: ---- RUNNING test.TestClass.test_1 ... -----

CRITICAL test: ----- PASSED -----

CRITICAL test: ---- RUNNING test.TestClass.test_2 ... -----

CRITICAL test: ----- FAILED -----

CRITICAL test: ---- RUNNING test.TestClass.test_3 ... -----

CRITICAL test: ----- PASSED -----

CRITICAL test: ---- RUNNING test.TestClass.test_4 ... -----

CRITICAL test: ----- FAILED -----

CRITICAL test: ---- RUNNING test.TestClass.test_5 ... -----

CRITICAL test: ----- ERRORED -----

背景

我从这个开始 :

class SmartTestCase(unittest.TestCase):

"""Knows its state (pass/fail/error) by the time its tearDown is called."""

def run(self, result):

# Store the result on the class so tearDown can behave appropriately

self.result = result.result if hasattr(result, 'result') else result

super(SmartTestCase, self).run(result)

@property

def errored(self):

return self.id() in [case.id() for case, _ in self.result.errors]

@property

def failed(self):

return self.id() in [case.id() for case, _ in self.result.failures]

@property

def passed(self):

return not (self.errored or self.failed)

但是,这仅在Python 2中有效。在Python 3(3.3及以下版本)中,控制流似乎有所改变:Python 3的unittest程序包在调用每个测试的tearDown()方法之后 处理结果 ……是否可以确认此行为?我们只需在测试类中增加一行(或六行)即可:

@@ -63,6 +63,12 @@

log.critical('----- FAILED -----')

else:

log.critical('----- PASSED -----')

+ log.warning(

+ 'ERRORS THUS FAR:\n'

+ + '\n'.join(tc.id() for tc, _ in self.result.errors))

+ log.warning(

+ 'FAILURES THUS FAR:\n'

+ + '\n'.join(tc.id() for tc, _ in self.result.failures))

if __name__ == '__main__':

然后只需重新运行测试:

$ python3.3 ./test.py -v

test_1 (__main__.TestClass) ... ok

test_2 (__main__.TestClass) ... FAIL

test_3 (__main__.TestClass) ... ok

test_4 (__main__.TestClass) ... FAIL

test_5 (__main__.TestClass) ... ERROR

[…]

…您将看到得到的结果是:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----

CRITICAL __main__: ----- PASSED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----

CRITICAL __main__: ----- PASSED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----

CRITICAL __main__: ----- PASSED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

__main__.TestClass.test_2

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----

CRITICAL __main__: ----- PASSED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

__main__.TestClass.test_2

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----

CRITICAL __main__: ----- PASSED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

__main__.TestClass.test_2

__main__.TestClass.test_4

现在,将以上内容与Python 2的输出进行比较:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_1 ... -----

CRITICAL __main__: ----- PASSED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_2 ... -----

CRITICAL __main__: ----- FAILED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

__main__.TestClass.test_2

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_3 ... -----

CRITICAL __main__: ----- PASSED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

__main__.TestClass.test_2

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_4 ... -----

CRITICAL __main__: ----- FAILED -----

WARNING __main__: ERRORS THUS FAR:

WARNING __main__: FAILURES THUS FAR:

__main__.TestClass.test_2

__main__.TestClass.test_4

CRITICAL __main__: ---- RUNNING __main__.TestClass.test_5 ... -----

CRITICAL __main__: ----- ERRORED -----

WARNING __main__: ERRORS THUS FAR:

__main__.TestClass.test_5

WARNING __main__: FAILURES THUS FAR:

__main__.TestClass.test_2

__main__.TestClass.test_4

由于Python 3在测试被result.failures 后会处理错误/失败,因此我们不能轻易在每种情况下都使用result.errors或result.failures来推断测试的结果。 (我认为从结构上讲, 在拆解测试后处理测试结果可能更有意义,但是,它确实是完全合理的用例,它根据测试的通过/失败状态遵循不同的测试结束程序很难见到...)

因此,我们可以参考其他人已经提到的_outcomeForDoCleanups ,而不是依赖于整体result对象,它包含当前正在运行的测试的结果对象,并具有必要的errors和failrues属性,可用于推断测试的结果。调用tearDown()时的状态:

@@ -3,6 +3,7 @@

from __future__ import unicode_literals

import logging

import os

+import sys

import unittest

@@ -16,6 +17,9 @@

log = logging.getLogger(__name__)

+PY = tuple(sys.version_info)[:3]

+

+

class SmartTestCase(unittest.TestCase):

"""Knows its state (pass/fail/error) by the time its tearDown is called."""

@@ -27,10 +31,14 @@

@property

def errored(self):

+ if PY >= (3, 0, 0):

+ return bool(self._outcomeForDoCleanups.errors)

return self.id() in [case.id() for case, _ in self.result.errors]

@property

def failed(self):

+ if PY >= (3, 0, 0):

+ return bool(self._outcomeForDoCleanups.failures)

return self.id() in [case.id() for case, _ in self.result.failures]

@property

这增加了对Python 3早期版本的支持。

但是,从Python 3.4开始,此私有成员变量不再存在 ,而是添加了一个新的(尽管也是私有的)方法: _feedErrorsToResult 。

这意味着对于版本3.4( 及更高版本 ),如果需求足够大,则可以( 非常骇客地 ) 强迫自己的方式使它再次像版本2中一样工作……

@@ -27,17 +27,20 @@

def run(self, result):

# Store the result on the class so tearDown can behave appropriately

self.result = result.result if hasattr(result, 'result') else result

+ if PY >= (3, 4, 0):

+ self._feedErrorsToResultEarly = self._feedErrorsToResult

+ self._feedErrorsToResult = lambda *args, **kwargs: None # no-op

super(SmartTestCase, self).run(result)

@property

def errored(self):

- if PY >= (3, 0, 0):

+ if (3, 0, 0) <= PY < (3, 4, 0):

return bool(self._outcomeForDoCleanups.errors)

return self.id() in [case.id() for case, _ in self.result.errors]

@property

def failed(self):

- if PY >= (3, 0, 0):

+ if (3, 0, 0) <= PY < (3, 4, 0):

return bool(self._outcomeForDoCleanups.failures)

return self.id() in [case.id() for case, _ in self.result.failures]

@@ -45,6 +48,10 @@

def passed(self):

return not (self.errored or self.failed)

+ def tearDown(self):

+ if PY >= (3, 4, 0):

+ self._feedErrorsToResultEarly(self.result, self._outcome.errors)

+

class TestClass(SmartTestCase):

@@ -64,6 +71,7 @@

self.assertHerp('Derp')

def tearDown(self):

+ super(TestClass, self).tearDown()

log.critical('---- RUNNING {} ... -----'.format(self.id()))

if self.errored:

log.critical('----- ERRORED -----')

…当然,所有此类的使用者都记得在各自的tearDown方法中使用super(…, self).tearDown() …

免责声明:纯粹是教育性的,请勿在家中尝试,等等。等等。我并不为此解决方案感到特别自豪,但是目前看来它已经足够好用了,这是我以后最好的解决方法在星期六下午摆弄一两个小时……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值