简体   繁体   English

Python unittest:如何临时将标准输出消息重定向到缓冲区并测试其内容?

[英]Python unittest: How to temporarily redirect stdout messages to a buffer and to test its content?

I would like to capture messages sent to stdout (our stderr ) temporarily during a test and assert if some string patterns occurred in these messages:我想在测试期间临时捕获发送到stdout (我们的stderr )的消息,并断言这些消息中是否出现了某些字符串模式:

import unittest
class SomeTest(unittest.TestCase):
    def test_stdout(self):
        output = ""
        function_that_writes_to_stdout()
        # How to capture stdout in output temporarily?
        self.assertIn("some message", output)

I found a similar question , but the accepted answer suggests to capture messages sent to stdout for all test cases.我发现了一个类似的问题,但接受的答案建议为所有测试用例捕获发送到stdout消息。

Yes, I know that it's not very smart to unittest messages sent to stdout.是的,我知道对发送到标准输出的消息进行单元测试并不是很聪明。 And yes, I also know that it'd be better to use logging in combination with assertLogs .是的,我也知道最好将loggingassertLogs结合使用。 Let's assume that both options are not available at this stage.让我们假设这两个选项在此阶段都不可用。

Solution 1. The following worked for me:解决方案1.以下对我有用:

import io
import unittest
from contextlib import redirect_stdout

class Test(unittest.TestCase):
    def test_stdout(self):
        buf = io.StringIO()
        with redirect_stdout(buf):
            print("foo!")
        self.assertIn("foo", buf.getvalue())

buf.getvalue() will contain the entire output, including \\n characters. buf.getvalue()将包含整个输出,包括\\n字符。

Solution 2. To mimic the behavior of assertLogs , one can extend unittest.TestCase by a method assertStdout as follows.解决方案 2.为了模仿assertLogs的行为,可以通过方法assertStdout扩展unittest.TestCase如下。

class StdoutRedirectionContext():
    class ListIO():
        def __init__(self):
            # Container for messages sent to stdout.
            self.output = []
        def write(self, s):
            # Filter empty strings or naked newline characters.
            if s in ("\n", ""): return
            self.output.append(s)

    def __enter__(self):
        self._buf = self.ListIO()
        self._ctx = redirect_stdout(self._buf)
        self._ctx.__enter__()
        return self._buf

    def __exit__(self, exc_type, exc_value, exc_traceback):
        self._ctx.__exit__(exc_type, exc_value, exc_traceback)

class TestCase(unittest.TestCase):
    def assertStdout(self):
        return StdoutRedirectionContext()

Here, StdoutRedirectionContext acts a context manager, and the single messages will be collected in the output list.在这里, StdoutRedirectionContext充当上下文管理器,单个消息将被收集在output列表中。 The extended TestCase can be used as follows to assert messages on stdout:扩展的TestCase可用于在 stdout 上断言消息,如下所示:

class AnotherTest(TestCase):
    def test_stdout(self):
        with self.assertStdout() as cm:
            print("foo!")
            print("bar!")
        self.assertIn("foo!", cm.output)
        self.assertIn("baz!", cm.output)

The above yields the following output:以上产生以下输出:

======================================================================
FAIL: test_stdout (__main__.AnotherTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "executor_test.py", line 440, in test_stdout
    self.assertIn("baz!", cm.output)
AssertionError: 'baz!' not found in ['foo!', 'bar!']

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM