Connecting django signal handlers in tests

Using django-cacheops, I want to verify that my views are cached as I expect them to be. In my test case, I connect a cache cache_read signal to the handler, which should increase the value in the cache for punches or misses. However, the signal never triggers. Does anyone know the correct way to include the django signal handler in the test file, exclusively for use in this test script?

that's what i still have

 from cacheops.signals import cache_read cache.set('test_cache_hits', 0) cache.set('test_cache_misses', 0) def cache_log(sender, func, hit, **kwargs): # never called if hit: cache.incr('test_cache_hits') else: cache.incr('test_cache_misses') class BootstrapTests(TestCase): @classmethod def setUpClass(cls): super(BootstrapTests, cls).setUpClass() cache_read.connect(cache_log) assert cache_read.has_listeners() def test_something_that_should_fill_and_retrieve_cache(self): .... hits = cache.get('test_cache_hits') # always 0 

I also tried connecting a signal handler at the module level and in the regular setUp test, all with the same result.

EDIT: Here is my actual test code, plus the object I'm testing. I use the cached_as decorator to cache the function. This test does not currently work.

boostrap.py

 class BootstrapData(object): def __init__(self, app, person=None): self.app = app def get_homepage_dict(self, context={}): url_name = self.app.url_name @cached_as(App.objects.filter(url_name=url_name), extra=context) def _get_homepage_dict(): if self.app.homepage is None: return None concrete_module_class = MODULE_MAPPING[self.app.homepage.type] serializer_class_name = f'{concrete_module_class.__name__}Serializer' serializer_class = getattr(api.serializers, serializer_class_name) concrete_module = concrete_module_class.objects.get(module=self.app.homepage) serializer = serializer_class(context=context) key = concrete_module_class.__name__ return { key: serializer.to_representation(instance=concrete_module) } return _get_homepage_dict() 

test_bootstrap.py

 class BootstrapDataTest(TestCase): def setUp(self): super(BootstrapDataTest, self).setUp() def set_signal(signal=None, **kwargs): self.signal_calls.append(kwargs) self.signal_calls = [] cache_read.connect(set_signal, dispatch_uid=1, weak=False) self.app = self.setup_basic_app() # creates an 'App' model and saves it def tearDown(self): cache_read.disconnect(dispatch_uid=1) def test_boostrap_data_is_cached(self): obj = BootstrapData(self.app) obj.get_homepage_dict() # fails, self.signal_calls == [] self.assertEqual(self.signal_calls, [{'sender': App, 'func': None, 'hit': False }]) self.signal_calls = [] obj.get_homepage_dict() self.assertEqual(self.signal_calls, [{'sender': App, 'func': None, 'hit': True}]) 
+7
python django caching unit-testing signals
source share
2 answers

In this particular case, it turned out that my test cases are subclasses of django rest framework APITestCase, which in turn are subclasses of django SimpleTestCase.

looking at caching sources, I found that these TransactionTestCase test subclasses and turning off the test case fix this problem.

It would be interesting to know why this is so, but the problem has been resolved.

0
source share

I don’t understand why this is happening, but I will try to make a useful answer anyway.

Firstly, if you want to check if the cache works, you should not rely on your own side effects to check this, and signals are side effects of its main function - preventing db calls. Try to verify that:

 def test_it_works(self): with self.assertNumQueries(1): obj.get_homepage_dict() with self.assertNumQueries(0): obj.get_homepage_dict() 

Secondly, if you want to know what is happening, you can insert fingerprints everywhere, including the caching code, and see where it stops. Alternatively, you can do a test for me, the instruction is here https://github.com/Suor/django-cacheops#writing-a-test .

Finally, your test is a bit wrong. For @cached_as() sender will be None , and the func function will be decorated.

+1
source share

All Articles