| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323 | 
							- .. _testing:
 
- ================================================================
 
-  Testing with Celery
 
- ================================================================
 
- Tasks and unit tests
 
- ====================
 
- To test task behavior in unit tests the preferred method is mocking.
 
- .. admonition:: Eager mode
 
-     The eager mode enabled by the :setting:`task_always_eager` setting
 
-     is by definition not suitable for unit tests.
 
-     When testing with eager mode you are only testing an emulation
 
-     of what happens in a worker, and there are many discrepancies
 
-     between the emulation and what happens in reality.
 
- A Celery task is much like a web view, in that it should only
 
- define how to perform the action in the context of being called as a task.
 
- This means optimally tasks only handle things like serialization, message headers,
 
- retries, and so on, with the actual logic implemented elsewhere.
 
- Say we had a task like this:
 
- .. code-block:: python
 
-     from .models import Product
 
-     @app.task(bind=True)
 
-     def send_order(self, product_pk, quantity, price):
 
-         price = Decimal(price)  # json serializes this to string.
 
-         # models are passed by id, not serialized.
 
-         product = Product.objects.get(product_pk)
 
-         try:
 
-             product.order(quantity, price)
 
-         except OperationalError as exc:
 
-             raise self.retry(exc=exc)
 
- You could write unit tests for this task, using mocking like
 
- in this example:
 
- .. code-block:: python
 
-     from pytest import raises
 
-     from celery.exceptions import Retry
 
-     # for python 2: use mock.patch from `pip install mock`.
 
-     from unittest.mock import patch
 
-     from proj.models import Product
 
-     from proj.tasks import send_order
 
-     class test_send_order:
 
-         @patch('proj.tasks.Product.order')  # < patching Product in module above
 
-         def test_success(self, product_order):
 
-             product = Product.objects.create(
 
-                 name='Foo',
 
-             )
 
-             send_order(product.pk, 3, Decimal(30.3))
 
-             product_order.assert_called_with(3, Decimal(30.3))
 
-         @patch('proj.tasks.Product.order')
 
-         @patch('proj.tasks.send_order.retry')
 
-         def test_failure(send_order_retry, product_order):
 
-             product = Product.objects.create(
 
-                 name='Foo',
 
-             )
 
-             # set a side effect on the patched method
 
-             # so that it raises the error we want.
 
-             product_order.side_effect = OperationalError()
 
-             with raises(Retry):
 
-                 send_order(product.pk, 3, Decimal(30.6))
 
- Py.test
 
- =======
 
- .. versionadded:: 4.0
 
- Celery is also a :pypi:`pytest` plugin that adds fixtures that you can
 
- use in your integration (or unit) test suites.
 
- Marks
 
- -----
 
- ``celery`` - Set test app configuration.
 
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
- The ``celery`` mark enables you to override the configuration
 
- used for a single test case:
 
- .. code-block:: python
 
-     @pytest.mark.celery(result_backend='redis://')
 
-     def test_something():
 
-         ...
 
- or for all the test cases in a class:
 
- .. code-block:: python
 
-     @pytest.mark.celery(result_backend='redis://')
 
-     class test_something:
 
-         def test_one(self):
 
-             ...
 
-         def test_two(self):
 
-             ...
 
- Fixtures
 
- --------
 
- Function scope
 
- ^^^^^^^^^^^^^^
 
- ``celery_app`` - Celery app used for testing.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- This fixture returns a Celery app you can use for testing.
 
- Example:
 
- .. code-block:: python
 
-     def test_create_task(celery_app, celery_worker):
 
-         @celery_app.task
 
-         def mul(x, y):
 
-             return x * y
 
-         assert mul.delay(4, 4).get(timeout=10) == 16
 
- ``celery_worker`` - Embed live worker.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- This fixture starts a Celery worker instance that you can use
 
- for integration tests.  The worker will be started in a *separate thread*
 
- and will be shutdown as soon as the test returns.
 
- Example:
 
- .. code-block:: python
 
-     # Put this in your confttest.py
 
-     @pytest.fixture(scope='session')
 
-     def celery_config():
 
-         return {
 
-             'broker_url': 'amqp://',
 
-             'result_backend': 'redis://'
 
-         }
 
-     def test_add(celery_worker):
 
-         mytask.delay()
 
-     # If you wish to override some setting in one test cases
 
-     # only - you can use the ``celery`` mark:
 
-     @pytest.mark.celery(result_backend='rpc')
 
-     def test_other(celery_worker):
 
-         ...
 
- Session scope
 
- ^^^^^^^^^^^^^
 
- ``celery_config`` - Override to setup Celery test app configuration.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- You can redefine this fixture to configure the test Celery app.
 
- The config returned by your fixture will then be used
 
- to configure the :func:`celery_app`, and :func:`celery_session_app` fixtures.
 
- Example:
 
- .. code-block:: python
 
-     @pytest.fixture(scope='session')
 
-     def celery_config():
 
-         return {
 
-             'broker_url': 'amqp://',
 
-             'result_backend': 'rpc',
 
-         }
 
- ``celery_parameters`` - Override to setup Celery test app parameters.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- You can redefine this fixture to change the ``__init__`` parameters of test
 
- Celery app. In contrast to :func:`celery_config`, these are directly passed to
 
- when instantiating :class:`~celery.Celery`.
 
- The config returned by your fixture will then be used
 
- to configure the :func:`celery_app`, and :func:`celery_session_app` fixtures.
 
- Example:
 
- .. code-block:: python
 
-     @pytest.fixture(scope='session')
 
-     def celery_parameters():
 
-         return {
 
-             'task_cls':  my.package.MyCustomTaskClass,
 
-             'strict_typing': False,
 
-         }
 
- ``celery_enable_logging`` - Override to enable logging in embedded workers.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- This is a fixture you can override to enable logging in embedded workers.
 
- Example:
 
- .. code-block:: python
 
-     @pytest.fixture(scope='session')
 
-     def celery_enable_logging():
 
-         return True
 
- ``celery_includes`` - Add additional imports for embedded workers.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- You can override fixture to include modules when an embedded worker starts.
 
- You can have this return a list of module names to import,
 
- which can be task modules, modules registering signals, and so on.
 
- Example:
 
- .. code-block:: python
 
-     @pytest.fixture(scope='session')
 
-     def celery_includes():
 
-         return [
 
-             'proj.tests.tasks',
 
-             'proj.tests.celery_signal_handlers',
 
-         ]
 
- ``celery_worker_pool`` - Override the pool used for embedded workers.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- You can override fixture to configure the execution pool used for embedded
 
- workers.
 
- Example:
 
- .. code-block:: python
 
-     @pytest.fixture(scope='session')
 
-     def celery_worker_pool():
 
-         return 'prefork'
 
- .. warning::
 
-     You cannot use the gevent/eventlet pools, that is unless your whole test
 
-     suite is running with the monkeypatches enabled.
 
- ``celery_session_worker`` - Embedded worker that lives throughout the session.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- This fixture starts a worker that lives throughout the testing session
 
- (it won't be started/stopped for every test).
 
- Example:
 
- .. code-block:: python
 
-     # Add this to your conftest.py
 
-     @pytest.fixture(scope='session')
 
-     def celery_config():
 
-         return {
 
-             'broker_url': 'amqp://',
 
-             'result_backend': 'rpc',
 
-         }
 
-     # Do this in your tests.
 
-     def test_add_task(celery_session_worker):
 
-         assert add.delay(2, 2) == 4
 
- .. warning::
 
-     It's probably a bad idea to mix session and ephemeral workers...
 
- ``celery_session_app`` - Celery app used for testing (session scope).
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- This can be used by other session scoped fixtures when they need to refer
 
- to a Celery app instance.
 
- ``use_celery_app_trap`` - Raise exception on falling back to default app.
 
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
- This is a fixture you can override in your ``conftest.py``, to enable the "app trap":
 
- if something tries to access the default or current_app, an exception
 
- is raised.
 
- Example:
 
- .. code-block:: python
 
-     @pytest.fixture(scope='session')
 
-     def use_celery_app_trap():
 
-         return True
 
- If a test wants to access the default app, you would have to mark it using
 
- the ``depends_on_current_app`` fixture:
 
- .. code-block:: python
 
-     @pytest.mark.usefixtures('depends_on_current_app')
 
-     def test_something():
 
-         something()
 
 
  |