Проверьте, что потребительский метод может вызвать исключение с помощью Django Channels и pytest-asyncio.
Используя Django и Channels 2, у меня есть потребительский метод, к которому можно обращаться через группы каналов и который может вызывать исключения. Как этот тривиальный:
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,
)
Я хочу проверить этот метод, используя pytest-asyncio. Поскольку можно поймать исключение сопрограммы с pytest.raises
Я наивно подумал, что такого будет достаточно
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'
})
Но тест проваливается довольно запутанным способом (усеченный вывод):
================== 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 =====
И что же мне делать? Является ли поднятие исключения из потребительского метода плохим дизайном?
1 ответ
channel_layer
имеет два сайта. Один сайт, который отправляет данные в channel_layer
и другой сайт, который получает данные. Отправляющий сайт не получает никакого ответа от принимающего сайта. Это означает, что если принимающий сайт вызывает исключение, отправляющий сайт его не видит.
В своем тесте вы тестируете отправляющий сайт. Отправляет сообщение channel_layer
, но, как объяснено, это не вызывает исключения.
Чтобы проверить, возникает ли исключение, вы должны написать тест, который подключается к вашему потребителю. Это может выглядеть так:
channel_layer = get_channel_layer()
@pytest.mark.asyncio
async def test_will_raise():
communicator = WebsocketCommunicator(DummyConsumer, "ws/dummy/")
await communicator.connect()
await channel_layer.group_send('dummy', {
'type': 'will_raise'
})
with pytest.raises(ValueError):
await communicator.wait()
Как видите, исключение не происходит при отправке в channel_layer
, но на коммуникаторе, который слушает на channel_layer
, Смотрите также: https://channels.readthedocs.io/en/latest/topics/testing.html
Также обратите внимание, что тест не вызывает communicator.disconnect()
, Когда исключение происходит внутри коммуникатора, disconnect()
не должен быть вызван. Смотрите второе предложение в зеленом поле "Важно" под заголовком: https://channels.readthedocs.io/en/latest/topics/testing.html.
Однако вам не нужно отключать (), если в вашем приложении уже возникла ошибка.