Search code examples
djangounit-testingdjango-haystack

Is it possible to disable django haystack for some tests?


We use django-haystack as our search index. Generally great, but during tests it adds overhead to every model object creation and save, and for most tests it is not required. So I would like to avoid it. So I thought I'd use override_settings to use a dummy that did nothing. But I've now tried both the BaseSignalProcessor and the SimpleEngine and I can still see our search index (elasticsearch) getting hit a lot.

The two version I have tried are:

First using the SimpleEngine which does no data preparation:

from django.test import TestCase
from django.test.utils import override_settings

HAYSTACK_DUMMY_INDEX = {
    'default': {
        'ENGINE': 'haystack.backends.simple_backend.SimpleEngine',
    }
}

@override_settings(HAYSTACK_CONNECTIONS=HAYSTACK_DUMMY_INDEX)
class TestAllTheThings(TestCase):
    # ...

and then using the BaseSignalProcessor which should mean that the signals to save are not hooked up:

from django.test import TestCase
from django.test.utils import override_settings

@override_settings(HAYSTACK_SIGNAL_PROCESSOR='haystack.signals.BaseSignalProcessor')
class TestAllTheThings(TestCase):
    # ...

I am using pytest as the test runner in case that matters.

Any idea if there is something I am missing?


Solution

  • The settings are only accessed once so overriding it after the fact won't change anything.

    Instead, you can subclass the signal processor and stick in some logic to conditionally disable it like so:

    from django.conf import settings
    
    from haystack.signals import BaseSignalProcessor
    
    
    class TogglableSignalProcessor(BaseSignalProcessor):
        settings_key = 'HAYSTACK_DISABLE'
    
        def handle_save(self, sender, instance, **kwargs):
            if not getattr(settings, self.settings_key, False):
                super().handle_save(sender, instance, **kwargs)
    
        def handle_delete(self, sender, instance, **kwargs):
            if not getattr(settings, self.settings_key, False):
                super().handle_delete(sender, instance, **kwargs)
    

    Now if you configure that as your signal processor then you can easily disable it in tests. The settings key can be set with an environment variable if you're just using manage.py test and not a custom runner. Otherwise you should know where to stick it.

    import os
    
    HAYSTACK_DISABLE = 'IS_TEST' in os.environ
    

    And run it with

    IS_TEST=1 python manage.py test
    

    And for the few tests where you want it enabled, use override_settings() like you have already tried:

    class MyTest(TestCase):
        @override_settings(HAYSTACK_ENABLE=True)
        def that_one_test_where_its_needed(self):
           pass
    

    Of course you can go even further and have conditional settings for the signal processor class so if you have a busy site then my conditional checks don't slow it down when it's running live.