You have two problem shere:
You're setting the return value of get_running_loop
, but an exception isn't a return value. If you want your mocked code to raise an exception, you need to configure a side_effect.
Your code catches the RuntimeError
and doesn't re-raise is: you simply set self.loop = None
and log an error. This means that even when you successfully raise a RuntimeError
from get_event_loop
, that exception will never be visible to your tests because it is consumed by your code.
If you were to mock your logger
object, you can check that logger.error
was called with the exception. E.g.:
@pytest.mark.asyncio
async def test_init_patch_runtime_error() -> None:
nest_asyncio.apply()
with patch("webrtc.logger") as mock_logger:
with patch("webrtc.get_running_loop", side_effect=RuntimeError()):
WebRTCConnection()
assert isinstance(mock_logger.error.call_args[0][0], RuntimeError)
Edit: W/r/t to checking the self.loop = None
part, I would probably rewrite the code like this:
class WebRTCConnection:
loop: Any = None
def __init__(self) -> None:
┆ try:
┆ ┆ self.loop = get_running_loop()
┆ except RuntimeError as e:
┆ ┆ logger.error(e)
┆ if self.loop is None:
┆ ┆ self.loop = asyncio.new_event_loop()
And then when testing, you would need to mock a return value for new_event_loop
. I would probably get rid of the nested with
statements and just use the patch
decorator on the function instead:
@pytest.mark.asyncio
@patch('webrtc.logger')
@patch('webrtc.get_running_loop', side_effect=RuntimeError())
@patch('webrtc.asyncio.new_event_loop', return_value='fake_loop')
async def test_init_patch_runtime_error(
mock_new_event_loop,
mock_get_running_loop,
mock_logger
) -> None:
nest_asyncio.apply()
rtc = WebRTCConnection()
assert isinstance(mock_logger.error.call_args[0][0], RuntimeError)
assert rtc.loop == 'fake_loop'
...but obviously you could do the same thing with either a series of nested with patch(...)
statements or a single long with
statement.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…