Проверьте, что потребительский метод может вызвать исключение с помощью Django Channels и pytest-asyncio. - PullRequest
0 голосов
/ 05 ноября 2018

Используя 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 Ответ

0 голосов
/ 11 ноября 2018

A 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#wait

Также обратите внимание, что тест не вызывает communicator.disconnect(). Когда в коммуникаторе возникает исключение, disconnect() вызывать не нужно. Смотрите второе предложение в зеленом поле «Важно» под этим заголовком: https://channels.readthedocs.io/en/latest/topics/testing.html#websocketcommunicator

Однако вам не нужно отключать (), если ваше приложение уже вызвало ошибку.

...