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?
StringIOdoes not have coroutine methods forread, so you can't mock this and have it work with your watch coroutine function (callinggetvalueon theStringIOinstance 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 asyncioStreamReaderinstance in your test and use thefeed_datamethod to write something to the stream. Then you can pass this in towatch. You can then use thecapsysfixture included with Pytest to capture whatwatchwrites to stdout.Below is an updated version of your code that passes as a standalone: