Skip to content

Commit aa91c28

Browse files
authored
Don't actually close DB connections during tests (#2101)
1 parent 5d8ddd9 commit aa91c28

File tree

7 files changed

+85
-7
lines changed

7 files changed

+85
-7
lines changed

channels/testing/__init__.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
from asgiref.testing import ApplicationCommunicator # noqa
2-
1+
from .application import ApplicationCommunicator # noqa
32
from .http import HttpCommunicator # noqa
43
from .live import ChannelsLiveServerTestCase # noqa
54
from .websocket import WebsocketCommunicator # noqa

channels/testing/application.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from unittest import mock
2+
3+
from asgiref.testing import ApplicationCommunicator as BaseApplicationCommunicator
4+
5+
6+
def no_op():
7+
pass
8+
9+
10+
class ApplicationCommunicator(BaseApplicationCommunicator):
11+
async def send_input(self, message):
12+
with mock.patch("channels.db.close_old_connections", no_op):
13+
return await super().send_input(message)
14+
15+
async def receive_output(self, timeout=1):
16+
with mock.patch("channels.db.close_old_connections", no_op):
17+
return await super().receive_output(timeout)

channels/testing/http.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from urllib.parse import unquote, urlparse
22

3-
from asgiref.testing import ApplicationCommunicator
3+
from channels.testing.application import ApplicationCommunicator
44

55

66
class HttpCommunicator(ApplicationCommunicator):

channels/testing/websocket.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import json
22
from urllib.parse import unquote, urlparse
33

4-
from asgiref.testing import ApplicationCommunicator
4+
from channels.testing.application import ApplicationCommunicator
55

66

77
class WebsocketCommunicator(ApplicationCommunicator):

docs/topics/testing.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ you might need to fall back to it if you are testing things like HTTP chunked
7373
responses or long-polling, which aren't supported in ``HttpCommunicator`` yet.
7474

7575
.. note::
76-
``ApplicationCommunicator`` is actually provided by the base ``asgiref``
77-
package, but we let you import it from ``channels.testing`` for convenience.
76+
``ApplicationCommunicator`` extends the class provided by the base ``asgiref``
77+
package. Channels adds support for running unit tests with async consumers.
7878

7979
To construct it, pass it an application and a scope:
8080

tests/conftest.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@
44

55
def pytest_configure():
66
settings.configure(
7-
DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3"}},
7+
DATABASES={
8+
"default": {
9+
"ENGINE": "django.db.backends.sqlite3",
10+
# Override Django’s default behaviour of using an in-memory database
11+
# in tests for SQLite, since that avoids connection.close() working.
12+
"TEST": {"NAME": "test_db.sqlite3"},
13+
}
14+
},
815
INSTALLED_APPS=[
916
"django.contrib.auth",
1017
"django.contrib.contenttypes",

tests/test_database.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from django import db
2+
from django.test import TestCase
3+
4+
from channels.db import database_sync_to_async
5+
from channels.generic.http import AsyncHttpConsumer
6+
from channels.generic.websocket import AsyncWebsocketConsumer
7+
from channels.testing import HttpCommunicator, WebsocketCommunicator
8+
9+
10+
@database_sync_to_async
11+
def basic_query():
12+
with db.connections["default"].cursor() as cursor:
13+
cursor.execute("SELECT 1234")
14+
return cursor.fetchone()[0]
15+
16+
17+
class WebsocketConsumer(AsyncWebsocketConsumer):
18+
async def connect(self):
19+
await basic_query()
20+
await self.accept("fun")
21+
22+
23+
class HttpConsumer(AsyncHttpConsumer):
24+
async def handle(self, body):
25+
await basic_query()
26+
await self.send_response(
27+
200,
28+
b"",
29+
headers={b"Content-Type": b"text/plain"},
30+
)
31+
32+
33+
class ConnectionClosingTests(TestCase):
34+
async def test_websocket(self):
35+
self.assertNotRegex(
36+
db.connections["default"].settings_dict.get("NAME"),
37+
"memorydb",
38+
"This bug only occurs when the database is materialized on disk",
39+
)
40+
communicator = WebsocketCommunicator(WebsocketConsumer.as_asgi(), "/")
41+
connected, subprotocol = await communicator.connect()
42+
self.assertTrue(connected)
43+
self.assertEqual(subprotocol, "fun")
44+
45+
async def test_http(self):
46+
self.assertNotRegex(
47+
db.connections["default"].settings_dict.get("NAME"),
48+
"memorydb",
49+
"This bug only occurs when the database is materialized on disk",
50+
)
51+
communicator = HttpCommunicator(
52+
HttpConsumer.as_asgi(), method="GET", path="/test/"
53+
)
54+
connected = await communicator.get_response()
55+
self.assertTrue(connected)

0 commit comments

Comments
 (0)