I can't seem to wrap my head around the issue here. I'm writing tests for my Channel Consumers following the description in the docs. I'd normally use the Django default unittest but since Channels requires using pytest, I'm using it but will still like to maintain the class structure of writing tests.
I had issues with writing the setup method similar to unittest which I would use to initialize the test attributes. I tried method fixtures, class fixtures using autouse=true, but none of them seemed to work as I couldn't access the instance attributes in the test methods. Finally I just decided to write the set_up
instance method and manually call it for each class. This is what I have so far:
@pytest.mark.django_db
@pytest.mark.asyncio
class TestChatConsumer(object):
@database_sync_to_async
def set_up(self):
self.user = UserFactory()
self.api_client = APIClientFactory()
self.token = TokenFactory(user=self.user)
self.credentials = {
'AUTHORIZATION': self.token.key,
...
}
self.credentials_params = '&'.join(['{}={}'.format(k, v) for k, v in self.credentials.items()])
self.authless_endpoint = '/api/v1/ws/'
self.endpoint = self.authless_endpoint + '?' + self.credentials_params
async def test_connect_without_credentials(self):
await self.set_up()
communicator = WebsocketCommunicator(application, self.authless_endpoint)
connected, subprotocol = await communicator.connect()
assert not connected
async def test_connect_with_credentials(self):
await self.set_up()
communicator = WebsocketCommunicator(application, self.endpoint)
connected, subprotocol = await communicator.connect()
assert connected
The issue is that I keep getting psycopg2.InterfaceError: connection already closed
when my consumer tries to access the database in it's code inside the connect
method. It uses database_sync_to_async
and works pretty well when I'm manually testing it.
Specifically, the line in the connect
method that raises the error looks like this:
await database_sync_to_async(user.inbox.increment_connections)().
Not sure what exactly the issue is as database_sync_to_async
should properly clean up old connections so that new ones can be properly created.
Any help will be very much appreciated.
Apparently I was looking at the wrong place. The issue was on this line:
await database_sync_to_async(user.inbox.increment_connections)()
.
inbox
is a related manager so it tries to fetch it before the actual database_sync_to_async
and it fails as it requires a db call.
I tried this await database_sync_to_async(user).inbox.increment_connections()
and it didn't work since it wraps a method, not an attribute so what I ended up doing was to use prefetch_related
to get the inbox
during the authentication. In that way user.inbox
no longer requires a db call and it works fine