Using Django and Channels 2, I have a consumer method that can can be accessed through channel groups and that may raise exceptions. Like this trivial one:
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
class DummyConsumer(WebsocketConsumer):
def connect(self):
async_to_sync(self.channel_layer.group_add)(
"dummy",
self.channel_name,
)
self.accept()
def will_raise(self, event):
raise ValueError('value error')
def disconnect(self, code):
async_to_sync(self.channel_layer.group_discard)(
"dummy",
self.channel_name,
)
I want to test this method using pytest-asyncio. Since one can catch the exception of a coroutine with pytest.raises
, I thought naively that something like this would be enough:
import pytest
from channels.testing import WebsocketCommunicator
from channels.layers import get_channel_layer
from app.consumers import DummyConsumer
channel_layer = get_channel_layer()
@pytest.fixture
async def communicator():
communicator = WebsocketCommunicator(DummyConsumer, "ws/dummy/")
await communicator.connect()
yield communicator
await communicator.disconnect()
@pytest.mark.asyncio
async def test_will_raise(communicator):
with pytest.raises(ValueError):
await channel_layer.group_send('dummy', {
'type': 'will_raise'
})
But the test fails in a pretty confusing way (truncated output):
================== ERRORS ==================
___ ERROR at teardown of test_will_raise ___
...
> raise ValueError('value error')
E ValueError: value error
app/consumers.py:28: ValueError
================= FAILURES =================
_____________ test_will_raise ______________
...
await channel_layer.group_send('dummy', {
> 'type': 'will_raise'
})
E Failed: DID NOT RAISE <class 'ValueError'>
app/tests_dummy.py:21: Failed
==== 1 failed, 1 error in 1.47 seconds =====
So, what should I do? Is the raising of an exception from a consumer method a bad design?