python - Test that a consumer method can raise an exception with Django Channels and pytest-asyncio
问题描述
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?
解决方案
Achannel_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
但是,如果您的应用程序已经引发错误,则不必断开连接()。
推荐阅读
- bitbucket-server - 如何创建一个拒绝使用错误文件名推送的 BitBucket 挂钩?
- image - 如何在 Flutter/Dart 中“将参数转换为 'dart.ui::Image'”
- java - Dropwizard 添加过滤器以记录相关 ID
- ios - MGLMapView 不显示添加到视图控制器的任何对象
- javascript - 从方法更新对象属性
- angular - 这个例子中的 this.emoji 是什么?
- coinbase-api - Commerce API 是否允许您接收查询(通过 Webhooks 或 REST),让您知道何时检测到付款?
- python - pyspark 中仅基于一列的两个 DataFrame 之间的差异
- android - Unity_play 来自 Android 内部存储的视频
- java - android studio 不会使用代码 android.os.Binder.execTransact(Binder.java:697) 安装应用程序