2011-09-19 42 views
36

我正在使用PyDev进行Python应用程序的开发和单元测试。 至于单元测试,一切工作都代表了内容记录到任何日志记录的事实。记录器不被PyDev的“捕获输出”捕获。PyDev单元测试:如何捕获记录到“捕获的输出”中的logging.Logger的文本

我已经着一切记录到这样的标准输出:

import sys 
logger = logging.getLogger() 
logger.level = logging.DEBUG 
logger.addHandler(logging.StreamHandler(sys.stdout)) 

尽管如此,“捕获输出”不显示的东西记录到记录器。

这里的示例单元测试脚本: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") 

控制台输出是:

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 

但是CAPTURED OUTPUT用于测试的是:

======================== CAPTURED OUTPUT ========================= 
AA 

有没有人知道如何在执行这个测试期间捕获所有记录到logging.Logger

回答

32

的问题是,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") 

虽然,一个更好的办法是添加/测试期间移除处理程序:

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) 
+8

为了完整:我需要为所有单元测试进行重定向。对我来说最好的解决方案是在setUp-method中添加新的处理程序,并在tearDown方法中删除它。 – gecco

+1

很好的回答,我[扩展](http://stackoverflow.com/a/15969985/321973)这是一个'__metaclass__',这样一个包装的'setUp'和'tearDown'自动包含这个 –

+1

为什么添加/删除处理程序而不是将其保留在测试用例之外? – mlt

14

我从小厌倦了手动添加Fabio's great code所有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) 

Now您的测试用例可以简单地继承LoggedTestCase,即class TestCase(LoggedTestCase)而不是class TestCase(unittest.TestCase),您就完成了。或者,您可以添加__metaclass__行,并在测试中定义logger或者稍微修改LogThisTestCase

+1

'__metaclass__'的真棒用法 – Randy

+1

@Randy谢谢,阅读[__metaclass__'这个伟大的解释](http://stackoverflow.com/a/6581949/321973)我只是_had_使用它... –

+0

我发现我自己上周也读了相同的答案,并且已经将其写入我的代码库中。 – Randy

5

我建议使用一个LogCapture和测试,你真的是记录你所期望的要记录:

http://testfixtures.readthedocs.org/en/latest/logging.html

+0

@Wooble - 对不起,将文档移动到RTD,所以它们现在位于:http://testfixtures.readthedocs.org/en/latest/ –

+0

我喜欢有一个包装来照顾它,所以不需要编写代码显然是一个共同的问题。但不幸的是,当我使用“print(l)”时,我常常使用记录器时遇到了UTF问题。所以我的方法是“sys.stdout.buffer.write(l .__ str __()。encode('utf8'))”。这很好,所以我想分享它。 –

+0

@IwanLD - 我推荐使用'LogCapture().check()'现在。你为什么不能使用它? –

1

我碰到这个问题也来了。我结束了StreamHandler的子类化,并使用一个获取sys.stdout的属性覆盖了stream属性。这样,处理程序将使用unittest.TestCase已交换到sys的流。标准输出:

class CapturableHandler(logging.StreamHandler): 

    @property 
    def stream(self): 
     return sys.stdout 

    @stream.setter 
    def stream(self, value): 
     pass 

然后,您可以设置运行测试,像这样前的日志处理程序(这将定制的处理程序添加到根logger):

def setup_capturable_logging(): 
    if not logging.getLogger().handlers: 
     logging.getLogger().addHandler(CapturableHandler()) 

如果像我一样,你有你的测试在单独的模块,你可以把每个单元测试模块的进口,这将确保日志记录设置后线测试运行之前:

import logutil 

logutil.setup_capturable_logging() 

这可能不是最干净的APPR噢,但它非常简单,对我来说效果很好。