首页 文章

丢弃被测程序的stdout / stderr,但保持unittest输出

提问于
浏览
5

我有这个示例代码( test_it.py ):

import sys

def give_me_5():
    print >>sys.stdout, "STDOUT"
    print >>sys.stderr, "STDERR"
    return 6

import unittest


class TestMe(unittest.TestCase):

    def setUp(self):
        pass

    def test_give_me_5(self):
        self.assertEqual(give_me_5(), 5)


if __name__ == '__main__':
    unittest.main()

这给了我以下输出:

» python -m unittest test_it
A long annoying output message
A long annoying error message
F
======================================================================
FAIL: test_give_me_5 (__main__.TestMe)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "xx.py", line 17, in test_give_me_5
    self.assertEqual(give_me_5(), 5)
AssertionError: 6 != 5

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

但由于被测程序产生的长而恼人的消息(真正的程序产生很多输出),我无法看到unittest的输出 . 我想摆脱正在测试的函数的stdout / stderr( give_me_5 ),但我仍然希望看到 unittest 的stdout / stderr . 我想得到这个结果:

» python -m unittest test_it
F
======================================================================
FAIL: test_give_me_5 (__main__.TestMe)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "xx.py", line 17, in test_give_me_5
    self.assertEqual(give_me_5(), 5)
AssertionError: 6 != 5

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)

因此,被测程序(stdout和stderr)产生的输出被unittest过滤掉, but the output produced by unittest itself is kept . 我不想修改被测试的代码(代码本身没有重定向) . 我只想告诉unittest所有输出到stdout / stderr的输出应该被丢弃 .

这可能吗?

3 回答

  • 4

    @Alik建议是对的 . 但我想它可以改进 .

    import sys
    # StringIO is replaced in Python 3:
    try:
        from StringIO import StringIO
    except ImportError:
        from io import StringIO
    
    class ReplaceStd(object):
        """ Let's make it pythonic. """
    
        def __init__(self):
            self.stdout = None
            self.stderr = None
    
        def __enter__(self):
            self.stdout = sys.stdout
            self.stderr = sys.stderr
    
            # as it was suggseted already:
            sys.stdout = StringIO()
            sys.stderr = StringIO()
    
        def __exit__(self, type, value, traceback):
            sys.stdout = self.stdout
            sys.stderr = self.stderr
    
    print('I am here')
    with ReplaceStd():
        print('I am not')
    
    print('I am back')
    

    并输出:

    I am here
    I am back
    
  • 5

    暂时将 sys.stdoutsys.stderr 替换为类似文件的实例 . 例如,您可以使用StringIO aka内存缓冲区 .

    from StringIO import StringIO
    
    .....
    
    
    class TestMe(unittest.TestCase):
    
        def setUp(self):
            pass
    
        def test_give_me_5(self):
    
            stdout = sys.stdout
            stderr = sys.stderr
    
            sys.stdout = StringIO()
            sys.stderr = StringIO()
    
            self.assertEqual(give_me_5(), 5)
    
            sys.stdout = stdout
            sys.stderr = stderr
    

    您可能希望添加异常处理,甚至可以在上下文管理器中将此代码转换为重用它

  • 0

    作为参考(和获得评论),这里是我最终使用的代码(灵感来自@Alik的回复):

    import sys
    
    def give_me_5():
        print >>sys.stdout, "A long annoying output message"
        print >>sys.stderr, "A long annoying error message"
        return 6
    
    
    import unittest
    
    def redirect(stdout_file=None, stderr_file=None):
        new_stdout = open(stdout_file, 'w') if stdout_file else None
        new_stderr = open(stderr_file, 'w') if stderr_file else None
        if new_stdout : sys.stdout = new_stdout
        if new_stderr : sys.stderr = new_stderr
    
    def redirect_testcase(test_case):
        logfile = '.'.join([test_case.__module__, test_case.__class__.__name__, 'out'])
        errfile = '.'.join([test_case.__module__, test_case.__class__.__name__, 'err'])
        redirect(logfile, errfile)
    
    
    class TestMe(unittest.TestCase):
    
        def setUp(self):
            redirect_testcase(self)
    
        def test_give_me_5(self):
            self.assertEqual(give_me_5(), 5)
    
    if __name__ == '__main__':
        unittest.main()
    

    现在只需执行 redirect_testcase(self) ,stdout / stderr重定向到 test_it.TestMe.out / test_it.TestMe.err ,并且unittest的输出在控制台stdout / stderr中可见(如果需要,可以通过shell重定向) .

    有一个问题(我还不知道如何修复):特定TestCase中的所有测试都将覆盖 .out / .err 文件 . 为每个测试打开一个不同的out / log文件会很好(而不是每个TestCase的常用文件)

相关问题