gec*_*cco 52 python logging unit-testing pydev
我正在使用PyDev进行Python应用程序的开发和单元测试.至于单元测试,一切都很好,除了没有内容记录到日志框架.PyDev的"捕获输出"未捕获记录器.
我已经将所有记录到标准输出的内容转发如下:
import sys
logger = logging.getLogger()
logger.level = logging.DEBUG
logger.addHandler(logging.StreamHandler(sys.stdout))
Run Code Online (Sandbox Code Playgroud)
然而,"捕获的输出"不会显示记录到记录器的东西.
这是一个示例unittest-script:test.py
import sys
import unittest
import logging
logger = logging.getLogger()
logger.level = logging.DEBUG
logger.addHandler(logging.StreamHandler(sys.stdout))
class TestCase(unittest.TestCase):
def testSimpleMsg(self):
print("AA")
logging.getLogger().info("BB")
Run Code Online (Sandbox Code Playgroud)
控制台输出是:
Finding files... done.
Importing test modules ... done.
testSimpleMsg (itf.lowlevel.tests.hl7.TestCase) ... AA
2011-09-19 16:48:00,755 - root - INFO - BB
BB
ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Run Code Online (Sandbox Code Playgroud)
但是测试的CAPTURED OUTPUT是:
======================== CAPTURED OUTPUT =========================
AA
Run Code Online (Sandbox Code Playgroud)
有人知道如何捕获logging.Logger
在执行此测试期间记录到的所有内容吗?
Fab*_*zny 57
问题是unittest
跑步者在测试开始之前替换sys.stdout
/ sys.stderr
,并且StreamHandler
仍在写入原始版本sys.stdout
.
如果将"当前"分配给sys.stdout
处理程序,它应该可以工作(请参阅下面的代码).
import sys
import unittest
import logging
logger = logging.getLogger()
logger.level = logging.DEBUG
stream_handler = logging.StreamHandler(sys.stdout)
logger.addHandler(stream_handler)
class TestCase(unittest.TestCase):
def testSimpleMsg(self):
stream_handler.stream = sys.stdout
print("AA")
logging.getLogger().info("BB")
Run Code Online (Sandbox Code Playgroud)
虽然,更好的方法是在测试期间添加/删除处理程序:
import sys
import unittest
import logging
logger = logging.getLogger()
logger.level = logging.DEBUG
class TestCase(unittest.TestCase):
def testSimpleMsg(self):
stream_handler = logging.StreamHandler(sys.stdout)
logger.addHandler(stream_handler)
try:
print("AA")
logging.getLogger().info("BB")
finally:
logger.removeHandler(stream_handler)
Run Code Online (Sandbox Code Playgroud)
Tob*_*ler 18
我从小厌倦了手动添加的法比奥的伟大的代码对所有setUp
S,所以我子类unittest.TestCase
的一些__metaclass__
ING:
class LoggedTestCase(unittest.TestCase):
__metaclass__ = LogThisTestCase
logger = logging.getLogger("unittestLogger")
logger.setLevel(logging.DEBUG) # or whatever you prefer
class LogThisTestCase(type):
def __new__(cls, name, bases, dct):
# if the TestCase already provides setUp, wrap it
if 'setUp' in dct:
setUp = dct['setUp']
else:
setUp = lambda self: None
print "creating setUp..."
def wrappedSetUp(self):
# for hdlr in self.logger.handlers:
# self.logger.removeHandler(hdlr)
self.hdlr = logging.StreamHandler(sys.stdout)
self.logger.addHandler(self.hdlr)
setUp(self)
dct['setUp'] = wrappedSetUp
# same for tearDown
if 'tearDown' in dct:
tearDown = dct['tearDown']
else:
tearDown = lambda self: None
def wrappedTearDown(self):
tearDown(self)
self.logger.removeHandler(self.hdlr)
dct['tearDown'] = wrappedTearDown
# return the class instance with the replaced setUp/tearDown
return type.__new__(cls, name, bases, dct)
Run Code Online (Sandbox Code Playgroud)
现在你的测试用例可以简单地继承LoggedTestCase
,即class TestCase(LoggedTestCase)
代替class TestCase(unittest.TestCase)
而你已经完成了.或者,您可以添加该__metaclass__
行并logger
在测试中定义或略微修改LogThisTestCase
.
nor*_*ius 14
有些人可能会访问此线程以找到将测试期间创建的日志转发到控制台或 PyDev 的方法。上面的答案已经提供了一些解决方案。
如果想在实际测试中捕获特定日志,我发现从 Python 3.4 开始,unittest.TestCase
提供了assertLogs()
,它返回一个捕获当前日志消息的上下文管理器。来自单元测试文档:
with self.assertLogs('foo', level='INFO') as cm:
logging.getLogger('foo').info('first message')
logging.getLogger('foo.bar').error('second message')
self.assertEqual(cm.output, ['INFO:foo:first message',
'ERROR:foo.bar:second message'])
Run Code Online (Sandbox Code Playgroud)
消息被捕获在cm.output
. 有关更详细的信息(如时间、文件、行号等),cm.records
包含LogRecords
.
所有这些并没有直接解决 PyDev 面临的 OP,而是提供了一种以编程方式检查创建的消息的方法。
对于熟悉pytest的人来说,可以使用--log-cli-level=LEVEL
标志将格式良好的日志消息转发到控制台(例如pytest --log-cli-level=info
)。
在阅读了this 和其他一些相关线程中的答案后(谢谢!),这是我放在一起的上下文管理器,它将捕获记录器的输出(如果有的话)。
from io import StringIO
import logging
class CaptureLogger:
"""Context manager to capture `logging` streams
Args:
- logger: 'logging` logger object
Results:
The captured output is available via `self.out`
"""
def __init__(self, logger):
self.logger = logger
self.io = StringIO()
self.sh = logging.StreamHandler(self.io)
self.out = ''
def __enter__(self):
self.logger.addHandler(self.sh)
return self
def __exit__(self, *exc):
self.logger.removeHandler(self.sh)
self.out = self.io.getvalue()
def __repr__(self):
return f"captured: {self.out}\n"
Run Code Online (Sandbox Code Playgroud)
用法示例:
logger = logging.getLogger()
msg = "Testing 1, 2, 3"
with CaptureLogger(logger) as cl:
logger.error(msg)
assert cl.out, msg+"\n"
Run Code Online (Sandbox Code Playgroud)
当 OP 要求将其放入捕获的 stdout 流时,您可以将其打印到 stdout in 中__exit__
,因此添加一行如下:
def __exit__(self, *exc):
self.logger.removeHandler(self.sh)
self.out = self.io.getvalue()
print(self.out)
Run Code Online (Sandbox Code Playgroud)
此解决方案的不同之处在于它将收集日志记录输出并在所有正常print()
调用(如果有)之后立即将其全部转储出来。所以它可能是也可能不是 OP 所追求的,但这对我的需求很有效。