2017-06-29 77 views
7

使用django-cacheops,我想測試我的視圖是否按照我的意圖進行緩存。在我的測試案例中,我將cacheops cache_read信號連接到一個處理程序,該處理程序應該在緩存中爲緩存或未命中增加一個值。但是,信號從未被觸發。有沒有人知道在測試用例中連接django信號處理程序的正確方法,純粹是爲了在測試用例中使用?在測試中連接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}]) 
+1

你確定測試會觸發'cache_read'信號嗎?不只是任何緩存讀取都會觸發cacheops「cache_read」信號。 https://github.com/Suor/django-cacheops/blob/master/cacheops/query.py 同樣在cachops包中,他們有一個測試例子,它們在'setUp'方法中連接一個信號。 https://github.com/Suor/django-cacheops/blob/daa907d6ec5dc98d5cc80a3d519469fb134bd0bb/tests/tests.py#L917 – ARJMP

+1

我可能是錯的,但django-cacheops被設計爲緩存查詢集而不是常規密鑰。因此,'cache_read'信號將不會被調用,因爲您試圖獲取的值不是查詢集。 – mattjegan

+0

你可能不會做緩存請求或永遠不會點擊。你應該提供你的測試代碼。 – Suor

回答

0

在這個特定的情況下,事實證明,我的測試用例將django rest框架的APITestCase繼承了下來,這繼承了django的SimpleTestCase的子類。

尋找cacheops的來源,我發現那些測試的子類TransactionTestCase,並將測試案例切換出來解決了這個問題。

有興趣知道爲什麼會出現這種情況,但問題目前已解決。

+0

這是因爲'SimpleTestCase'將每個測試包裝到一個事務中,並且一旦它處於骯髒狀態,cacheops在事務中關閉。這是因爲從事務內可見的db狀態是不同的,不應該被緩存也不依賴於緩存。 – Suor

+0

啊哈,謝謝你的澄清 – bharling

1

我不明白爲什麼會這樣,但我會努力做一個有用的答案呢。

首先,如果你想測試緩存是否工作,你不應該依靠自己的副作用來檢查它,並且信號是其主要功能的副作用 - 防止數據庫調用。嘗試爲:

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

    with self.assertNumQueries(0): 
     obj.get_homepage_dict() 

第二,如果你想知道發生了什麼事你添加打印無處不在,包括cacheops代碼可挖,看看它停止。或者,你可以做一個測試讓我看到,指令在這裏https://github.com/Suor/django-cacheops#writing-a-test

最後,你的測試有點不對。對於@cached_as()發件人將是None和func將被裝飾的功能。

+0

感謝您的幫助 - 幾個月後回來,我瞭解到,這是因爲我的測試用例沒有使用TransactionTestCase作爲它的基礎(就像在緩存中進行測試一樣)。切換測試用例基類使其全部工作。 – bharling