django / channels

Developer-friendly asynchrony for Django
https://channels.readthedocs.io
BSD 3-Clause "New" or "Revised" License
6.06k stars 800 forks source link

Daphne still needed to use channels.testing #1942

Open bertonha opened 1 year ago

bertonha commented 1 year ago

on channels.testing module it import all Consumers and TestCases, but the ChannelsLiveServerTestCase imports daphne, if you decided to not have daphne testing is completely not usable.

https://github.com/django/channels/blob/main/channels/testing/__init__.py#L4

https://github.com/django/channels/blob/main/channels/testing/live.py#L3

tests/websocket/utils.py:2: in <module>
    from channels.testing import WebsocketCommunicator
../../../.local/share/virtualenvs/test-channels/lib/python3.10/site-packages/channels/testing/__init__.py:4: in <module>
    from .live import ChannelsLiveServerTestCase  # noqa
../../../.local/share/virtualenvs/test-channels/lib/python3.10/site-packages/channels/testing/live.py:3: in <module>
    from daphne.testing import DaphneProcess
E   ModuleNotFoundError: No module named 'daphne'
carltongibson commented 1 year ago

Sure, this was noted in the release notes. The test case depends on the Daphne process to run the web server.

bertonha commented 1 year ago

But if we comment the import of ChannelsLiveServerTestCase from __init__.py and use the WebsocketCommunicator. It works perfectly.

Maybe we could fail the use of ChannelsLiveServerTestCase if Daphne is not installed

carltongibson commented 1 year ago

For the moment I'd suggest importing the communicator from the submodule so you're not blocked.

bertonha commented 1 year ago

Hello @carltongibson I really dunno if is possible to import the WebsocketCommunicator without the execution of channels/testing/__init__.py which it needs daphne installed.

both way of importing resulting in the exception:

from channels.testing import WebsocketCommunicator
from channels.testing.websocket import WebsocketCommunicator
ModuleNotFoundError: No module named 'daphne'
carltongibson commented 1 year ago

Yep, you're right:

>>> from channels.testing.http import HttpCommunicator
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/carlton/Envs/tmp-5e851afa3eddf65/lib/python3.10/site-packages/channels/testing/__init__.py", line 4, in <module>
    from .live import ChannelsLiveServerTestCase  # noqa
  File "/Users/carlton/Envs/tmp-5e851afa3eddf65/lib/python3.10/site-packages/channels/testing/live.py", line 3, in <module>
    from daphne.testing import DaphneProcess
ModuleNotFoundError: No module named 'daphne'

We'll have to make that conditional along the lines of your PR. 👍

carltongibson commented 1 year ago

In the meantime you'll need to install Daphne for testing.

philippks commented 1 year ago

As a workaround, one can mock the channels.testing.live module, when it is not used in the tests:

import sys
from unittest.mock import MagicMock

sys.modules["channels.testing.live"] = MagicMock()

For me this works for using the WebsocketCommunicator.

johnthagen commented 1 year ago

A downside of this issue is that daphne brings with it a large dependency tree (mostly due to twisted), which increases install time for local developers and in CI for something that otherwise isn't needed if you are just using something like the WebsocketCommunicator for testing.

35 total dependencies:

daphne 4.0.0 Django ASGI (HTTP/WebSocket) server
├── asgiref >=3.5.2,<4
├── autobahn >=22.4.2
│   ├── cryptography >=3.4.6 
│   │   └── cffi >=1.12 
│   │       └── pycparser * 
│   ├── hyperlink >=21.0.0 
│   │   └── idna >=2.5 
│   ├── setuptools * 
│   └── txaio >=21.2.1 
└── twisted >=22.4
    ├── attrs >=19.2.0 
    ├── automat >=0.8.0 
    │   ├── attrs >=19.2.0 (circular dependency aborted here)
    │   └── six * 
    ├── constantly >=15.1 
    ├── hyperlink >=17.1.1 
    │   └── idna >=2.5 
    ├── idna >=2.4 (circular dependency aborted here)
    ├── incremental >=21.3.0 
    ├── pyopenssl >=21.0.0 
    │   └── cryptography >=38.0.0,<41 
    │       └── cffi >=1.12 
    │           └── pycparser * 
    ├── service-identity >=18.1.0 
    │   ├── attrs >=19.1.0 (circular dependency aborted here)
    │   ├── cryptography * (circular dependency aborted here)
    │   ├── pyasn1 * 
    │   ├── pyasn1-modules * 
    │   │   └── pyasn1 >=0.4.6,<0.6.0 (circular dependency aborted here)
    │   └── six * (circular dependency aborted here)
    ├── twisted-iocpsupport >=1.0.2,<2 
    ├── typing-extensions >=3.6.5 
    └── zope-interface >=4.4.2 
        └── setuptools * 

Making the import conditional as mentioned above would alleviate this issue.

carltongibson commented 1 year ago

Yes, it was this concern that led to making Daphne optional in the first place. 👍

johnthagen commented 1 year ago

I was able to confirm that @philippks's workaround works. ❤️

For pytest users, adding the following code to conftest.py will fix the issue for pytest invocations.

import sys
from unittest.mock import MagicMock

sys.modules["channels.testing.live"] = MagicMock()