I am trying to mock.patch sys.stdout to StringIO as a decorator to record the output for testing.
As a 'with' statement it works this way:
with mock.patch('sys.stdout', new_callable = StringIO) as recorded_output:
print('OUTPUT')
r = recorded_output.getvalue()
print(r)
gets 'OUTPUT', but 'getvalue' doesn't work when used as a decorator:
@mock.patch('sys.stdout', new_callable = StringIO)
def test_stdout(self, recorded_output):
print('OUTPUT')
r = recorded_output.getvalue()
print(r)
gets error: Mock object has no attribute 'getvalue'
Does anyone know how to write this as a decorator?
-
2Pytest provides a set of fixtures to capture stdout already; can you use one of these for your test?David Maze– David Maze2025年09月12日 11:13:12 +00:00Commented Sep 12 at 11:13
-
1I don't know why you get this error (I don't when I try it), but your last print won't work anyway since stdout is still mocked at this point.Viper– Viper2025年09月12日 12:16:49 +00:00Commented Sep 12 at 12:16
1 Answer 1
There is an example in mock documentation that does exactly what you want (and I bet you already read it according to your code :-))
The example is pretty much like this
from io import StringIO
from unittest.mock import patch
def foo():
print('Something')
@patch('sys.stdout', new_callable=StringIO)
def test(mock_stdout):
foo()
assert mock_stdout.getvalue() == 'Something\n'
test()
But, you are trying to use the print function inside the mock, and it is not a directly call to the sys.stdout. A good way to handle with this issue is to add a mock for the print function as the following example.
from io import StringIO
from unittest.mock import patch, call
@patch('builtins.print')
def test_print(mocked_print):
print('Something')
print()
assert mocked_print.mock_calls == [call('Something'), call()]
But if you want to mix the both approaches, then David Maze proposal's certainly is the better way to handle with it.