I am trying to write a pytest testcase for a asyncio function which does read the output streams (stderr/stdout) and modifies the lines. The function I want to test (which again gets called inside asyncio.gather
) is as shown below:
import asyncio
async def watch(stream):
while True:
lines = await stream.read(2**16)
if not lines or lines == "":
break
lines = lines.strip().split("\n")
for line in lines:
print(f'myPrefix-{line}')
The pytest testcase I wrote is as follows:
import asyncio
from io import StringIO
import pytest
@pytest.fixture(autouse=True)
def event_loop():
loop = asyncio.get_event_loop()
yield loop
loop.close()
@pytest.mark.asyncio
async def test_watch(event_loop):
expected_outcome = "myPrefix-This is stdout"
def write_something():
print("This is stdout")
with patch("sys.stdout", new=StringIO()) as mock_stdout:
write_something()
output = await watch(mock_stdout.getvalue())
assert output.return_value == expected_outcome
However, when I execute this pytest I encounter AttributeError: 'str' object has no attribute 'read'
. How to test asyncio coroutines while dealing with stdout/stderr streams?
StringIO
does not have coroutine methods forread
, so you can't mock this and have it work with your watch coroutine function (callinggetvalue
on theStringIO
instance also just passes in the string written to stdout, which explains the error you get). Assuming that the stream in your watch function is an instance ofStreamReader
, you can just create an asyncioStreamReader
instance in your test and use thefeed_data
method to write something to the stream. Then you can pass this in towatch
. You can then use thecapsys
fixture included with Pytest to capture whatwatch
writes to stdout.Below is an updated version of your code that passes as a standalone: