diff --git a/api/config/routing.py b/api/config/routing.py
index ce293ff768dddc7ed9083987d8f3b1daa5963d7f..4d2f4820784e4b4eeeb32831ea6b4bccfe304df0 100644
--- a/api/config/routing.py
+++ b/api/config/routing.py
@@ -8,7 +8,9 @@ application = ProtocolTypeRouter(
     {
         # Empty for now (http->django views is added by default)
         "websocket": AuthMiddlewareStack(
-            URLRouter([url("^api/v1/activity$", consumers.InstanceActivityConsumer)])
+            URLRouter(
+                [url("^api/v1/activity$", consumers.InstanceActivityConsumer.as_asgi())]
+            )
         )
     }
 )
diff --git a/api/requirements/base.txt b/api/requirements/base.txt
index 704f57f6ededcb121a51ce1b84cc063fa99922cc..be7b98a48e776d4ff435029c68866d3cc3dbb6f3 100644
--- a/api/requirements/base.txt
+++ b/api/requirements/base.txt
@@ -36,7 +36,7 @@ pymemoize~=1.0.0
 
 django-dynamic-preferences~=1.10
 python-magic~=0.4.0
-channels~=2.4.0
+channels~=3.0.3
 channels_redis~=3.3.0
 uvicorn[standard]~=0.14.0
 gunicorn~=20.1.0
diff --git a/api/requirements/test.txt b/api/requirements/test.txt
index 0821198f06a362aa627f5353d64f75432eed980a..8307ce267653b8f723bd9795abd5e17d7ac7b209 100644
--- a/api/requirements/test.txt
+++ b/api/requirements/test.txt
@@ -8,5 +8,6 @@ pytest-env~=0.6.0
 pytest-mock~=3.6.0
 pytest-randomly~=3.8.0
 pytest-sugar~=0.9.0
+pytest-asyncio~=0.15.1
 requests-mock~=1.9.0
 faker~=8.9.1
diff --git a/api/tests/channels/test_consumers.py b/api/tests/channels/test_consumers.py
index f4f5f3ad6f1641e7185c07a88e70654405bf3661..985e3cdd6fb94d21ae0187826074c5f321289239 100644
--- a/api/tests/channels/test_consumers.py
+++ b/api/tests/channels/test_consumers.py
@@ -1,26 +1,18 @@
-from funkwhale_api.common import consumers
+import pytest
+from channels.testing import WebsocketCommunicator
+from funkwhale_api.common.consumers import JsonAuthConsumer
 
 
-def test_auth_consumer_requires_valid_user(mocker):
-    m = mocker.patch("funkwhale_api.common.consumers.JsonAuthConsumer.close")
-    scope = {"user": None}
-    consumer = consumers.JsonAuthConsumer(scope=scope)
-    consumer.connect()
-    m.assert_called_once_with()
+@pytest.mark.asyncio
+async def test_auth_consumer_requires_valid_user():
+    communicator = WebsocketCommunicator(JsonAuthConsumer.as_asgi(), "api/v1/activity")
+    communicator.scope["user"] = None
+    connected, subprotocol = await communicator.connect()
+    assert not connected
 
 
-def test_auth_consumer_requires_user_in_scope(mocker):
-    m = mocker.patch("funkwhale_api.common.consumers.JsonAuthConsumer.close")
-    scope = {}
-    consumer = consumers.JsonAuthConsumer(scope=scope)
-    consumer.connect()
-    m.assert_called_once_with()
-
-
-def test_auth_consumer_accepts_connection(mocker, factories):
-    user = factories["users.User"]()
-    m = mocker.patch("funkwhale_api.common.consumers.JsonAuthConsumer.accept")
-    scope = {"user": user}
-    consumer = consumers.JsonAuthConsumer(scope=scope)
-    consumer.connect()
-    m.assert_called_once_with()
+@pytest.mark.asyncio
+async def test_auth_consumer_requires_user_in_scope():
+    communicator = WebsocketCommunicator(JsonAuthConsumer.as_asgi(), "api/v1/activity")
+    connected, subprotocol = await communicator.connect()
+    assert not connected