在测试中连接django信号处理程序

时间:2017-06-29 09:17:17

标签: python django unit-testing caching signals

使用django-cacheops,我想测试我的视图是否正在按照我的意图进行缓存。在我的测试用例中,我将cacheops cache_read信号连接到一个处理程序,该处理程序应该在缓存中为命中或未命中增加一个值。但是,信号永远不会被触发。有没有人知道在测试用例中连接django信号处理程序的正确方法,纯粹用于该测试用例?

这是我到目前为止所拥有的

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

我还尝试在模块级别和常规测试用例setUp方法中连接信号处理程序,所有这些都具有相同的结果。

编辑: 这是我的实际测试代码,以及我正在测试的对象。我使用cached_as装饰器来缓存一个函数。此测试目前失败。

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}])

2 个答案:

答案 0 :(得分:1)

我不明白为什么会这样,但无论如何我都会尝试做出有用的答案。

首先,如果你想测试缓存是否有效,你不应该依靠它自己的副作用来检查它,而且信号是它的主要功能的副作用 - 阻止数据库调用。尝试测试:

System.Drawing.Size

其次,如果你想知道发生了什么,你可能会在任何地方添加打印件,包括缓存代码,并查看它停止的位置。或者,你可以让我看一下测试,指令在这里https://github.com/Suor/django-cacheops#writing-a-test

最后,你的测试有点不对劲。对于def test_it_works(self): with self.assertNumQueries(1): obj.get_homepage_dict() with self.assertNumQueries(0): obj.get_homepage_dict() ,发件人将为@cached_as(),而func将为装饰功能。

答案 1 :(得分:1)

在这个特定情况下,结果是我的测试用例子类化了django rest框架的APITestCase,后者继承了django的SimpleTestCase。

查看缓存中的源代码,我发现那些测试是子类TransactionTestCase,并且切换出测试用例修复了这个问题。

有兴趣知道为什么会这样,但问题现在已经解决了。