gpt4 book ai didi

Django channel 2,在测试中访问数据库

转载 作者:行者123 更新时间:2023-12-01 11:17:16 29 4
gpt4 key购买 nike

我最近将我的项目更新为 Django 2 和 channel 2。现在我正在尝试重写我的聊天应用程序测试。

我面临依赖于 pytest-django 的 django db 标记的测试问题。我尝试在 fixture 、设置方法、测试函数本身中创建对象,在 WebsocketCommunicator 上使用 async_to_sync .然而,这些都没有奏效。

如果我在 fixture 中创建一个用户并正确保存它会得到一个 id。但是,在我的消费者 Django 中,在数据库中看不到该用户。并将其视为匿名用户。

我有一个临时 token ,用于在 websocket.connect 上对用户进行身份验证。

@pytest.fixture
def room():
room = generate_room()
room.save()
return room


@pytest.fixture
def room_with_user(room, normal_user):
room.users.add(normal_user)
yield room
room.users.remove(normal_user)

@pytest.fixture
def normal_user():
user = generate_user()
user.save()
return user

@pytest.mark.django_db
class TestConnect:

@pytest.mark.asyncio
async def test_get_connected_client(self, path, room_with_user, temp_token):
assert get_path(room_with_user.id) == path

communicator = QSWebsocketCommunicator(application, path, query_string=get_query_string(temp_token))
connected, subprotocol = await communicator.connect()
assert connected
await communicator.disconnect()

消费者:
class ChatConsumer(JsonWebsocketConsumer):
def connect(self):
# Called on connection. Either call

self.user = self.scope['user']

self.room_id = self.scope['url_route']['kwargs']['room_id']
group = f'room_{self.room_id}'
users = list(User.objects.all()) # no users here
self.group_name = group

if not (self.user is not None and self.user.is_authenticated):
return self.close({'Error': 'Not authenticated user'})

try:
self.room = Room.objects.get(id=self.room_id, users__id=self.user.id)
except ObjectDoesNotExist:
return self.close({'Error': 'Room does not exists'})

# Send success response
self.accept()

# Save user as active
self.room.active_users.add(self.user)

我的认证中间件
class OAuthTokenAuthMiddleware:
"""
Custom middleware that takes Authorization header and read OAuth token from it.
"""

def __init__(self, inner):
# Store the ASGI application we were passed
self.inner = inner

def __call__(self, scope):
temp_token = self.get_token(scope)
scope['user'] = self.validate_token(temp_token)
return self.inner(scope)

@staticmethod
def get_token(scope) -> str:
return url_parse.parse_qs(scope['query_string'])[b'token'][0].decode("utf-8")

@staticmethod
def validate_token(token):
try:
token = TemporaryToken.objects.select_related('user').get(token=token)
if token.is_active():
token.delete()
return token.user
else:
return AnonymousUser()
except ObjectDoesNotExist:
return AnonymousUser()

以及接受 query_string 以包含我的一次性 token 的自定义 WebsocketCommunicator
class QSWebsocketCommunicator(WebsocketCommunicator):
def __init__(self, application, path, headers=None, subprotocols=None,
query_string: Optional[Union[str, bytes]]=None):
if isinstance(query_string, str):
query_string = str.encode(query_string)
self.scope = {
"type": "websocket",
"path": path,
"headers": headers or [],
"subprotocols": subprotocols or [],
"query_string": query_string or ''
}
ApplicationCommunicator.__init__(self, application, self.scope)

我的问题是如何在测试/装置中创建用户、房间等对象,以便我可以在 Django 消费者中访问它们。

或者你有其他想法我怎样才能克服这个?

最佳答案

使用您提供的代码几乎不可能重现您的问题。阅读 How to create a Minimal, Complete, and Verifiable example .但是,我认为您应该在测试中使用真实交易作为普通 pytest.mark.django_db将跳过事务并且不会在数据库中存储任何数据本身。一个工作示例:

# routing.py

from django import http
from django.conf.urls import url
from django.contrib.auth.models import User
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.generic.websocket import JsonWebsocketConsumer

class ChatConsumer(JsonWebsocketConsumer):
def connect(self):
self.user = self.scope['user']
print('user in scope, set by middleware:', self.user)
users = list(User.objects.all()) # no users here
print('all users in chat consumer:', users)

if not (self.user is not None and self.user.is_authenticated):
return self.close({'Error': 'Not authenticated user'})

# Send success response
self.accept()


class OAuthTokenAuthMiddleware:
def __init__(self, inner):
# Store the ASGI application we were passed
self.inner = inner

def __call__(self, scope):
token = self.get_token(scope)
print('token in middleware:', token)
scope['user'] = User.objects.get(username=token)
return self.inner(scope)

@staticmethod
def get_token(scope) -> str:
d = http.QueryDict(scope['query_string'])
return d['token']


APP = ProtocolTypeRouter({
'websocket': OAuthTokenAuthMiddleware(URLRouter([url(r'^websocket/$', ChatConsumer)])),
})

创建用户名 spam 的示例 fixture :
@pytest.fixture(scope='function', autouse=True)
def create_user():
with transaction.atomic():
User.objects.all().delete()
user = User.objects.create_user(
'spam', 'spam@example.com', password='eggs',
first_name='foo', last_name='bar'
)
return user

现在,我将测试标记为事务性测试,这意味着每个查询实际上都已提交。现在测试用户被存储到数据库中,并且在中间件/消费者中进行的查询实际上可以返回一些有意义的东西:
@pytest.mark.django_db(transaction=True)
@pytest.mark.asyncio
async def test_get_connected_client():
app = OAuthTokenAuthMiddleware(URLRouter([url(r'^websocket/$', ChatConsumer)]))
communicator = QSWebsocketCommunicator(app, '/websocket/', query_string='token=spam')
connected, subprotocol = await communicator.connect()
assert connected
await communicator.disconnect()

运行测试测试会产生所需的结果:

$ pytest -vs
================================== test session starts =================================
platform darwin -- Python 3.6.3, pytest-3.4.0, py-1.5.2, pluggy-0.6.0 -- /Users/hoefling/.virtualenvs/stackoverflow/bin/python
cachedir: .pytest_cache
Django settings: spam.settings (from environment variable)
rootdir: /Users/hoefling/projects/private/stackoverflow/so-49136564/spam, inifile: pytest.ini
plugins: celery-4.1.0, forked-0.2, django-3.1.2, cov-2.5.1, asyncio-0.8.0, xdist-1.22.0, mock-1.6.3, hypothesis-3.44.4
collected 1 item

tests/test_middleware.py::test_get_connected_client Creating test database for alias 'default'...
token in middleware: spam
user in scope: spam
all users in chat consumer: [<User: spam>]
PASSEDDestroying test database for alias 'default'...


=============================== 1 passed in 0.38 seconds ================================

顺便说一句,你不需要绕过 WebsocketCommunicator不再,因为它现在能够处理查询字符串,请参阅 this issue closed .

关于Django channel 2,在测试中访问数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49136564/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com