2015-09-28 18 views
1

這種模式是從Django文檔追蹤:的Django測試:看到錯誤的反應時生成

class SimpleTest(unittest.TestCase): 
    def test_details(self): 
     client = Client() 
     response = client.get('/customer/details/') 
     self.assertEqual(response.status_code, 200) 

來源:https://docs.djangoproject.com/en/1.8/topics/testing/tools/#default-test-client

如果測試失敗,該錯誤信息不會很大幫助。例如,如果status_code是302,那麼我看到302 != 200

現在的問題是:哪裏錯誤的HTTPResponse被創建?

我希望看到解釋器的棧跟蹤創建錯誤的HTTPResponse對象。

我閱讀assertions of django的文檔,但沒有找到匹配的方法。

更新

這是一個普遍的問題:如何馬上看到想要的信息,如果斷言失敗?由於這些斷言(self.assertEqual(response.status_code, 200))很常見,我不想開始調試。

更新2016

我再有同樣的想法,發現目前的答案不是100%容易。我寫了一個新的答案,它具有簡單易用(Django的web客戶端的子類)的解決方案:Django: assertEqual(response.status_code, 200): I want to see useful stack of functions calls

+0

你是什麼意思,錯誤的迴應? 404告訴你要麼/ customer/details /沒有被匹配爲URL,要麼被明確返回的視圖返回404. –

+0

@DanielRoseman是的,我是對的。狀態碼404並不是一個好例子。但問題依然存在。我將錯誤的狀態碼更改爲302. – guettli

+1

通常情況下,302意味着您錯過了URL的尾部斜線(例如,它正在從/ customer/details到'/ customer/details /'重定向。如果沒有,您需要查看視圖並查看其返回重定向的位置。測試客戶端沒有響應的回溯,因爲沒有產生響應的錯誤;它只是有一個不同於你預期的狀態碼。 – Alasdair

回答

1

我認爲它可能會創建一個TestCase子類,monkeypatches django.http.response.HttpResponseBase.__init__()記錄堆棧跟蹤並將其存儲在Response對象上,然後寫一個assertResponseCodeEquals(response, status_code=200)方法,打印失敗的存儲堆棧跟蹤顯示,其中Response是可以實現創建。

我真的可以真正爲自己使用一個解決方案,並且可能會考慮實現它。

更新: 這裏是v1實現,它可以使用一些改進(例如,只打印堆棧跟蹤的相關行)。

import mock 
from traceback import extract_stack, format_list 
from django.test.testcases import TestCase 
from django.http.response import HttpResponseBase 

orig_response_init = HttpResponseBase.__init__ 

def new_response_init(self, *args, **kwargs): 
    orig_response_init(self, *args, **kwargs) 
    self._init_stack = extract_stack() 

class ResponseTracebackTestCase(TestCase): 
    @classmethod 
    def setUpClass(cls): 
     cls.patcher = mock.patch.object(HttpResponseBase, '__init__', new_response_init) 
     cls.patcher.start() 

    @classmethod 
    def tearDownClass(cls): 
     cls.patcher.stop() 

    def assertResponseCodeEquals(self, response, status_code=200): 
     self.assertEqual(response.status_code, status_code, 
      "Response code was '%s', expected '%s'" % (
       response.status_code, status_code, 
      ) + '\n' + ''.join(format_list(response._init_stack)) 
     ) 

class MyTestCase(ResponseTracebackTestCase): 
    def test_index_page_returns_200(self): 
     response = self.client.get('/') 
     self.assertResponseCodeEquals(response, 200) 
1

如何查看追溯,如果斷言失敗沒有調試

如果斷言失敗,沒有回溯。 client.get()沒有失敗,它只是返回了與您期望的不同的響應。

您可以使用pdb來逐步調用client.get()調用,並瞭解它爲何返回意外響應。

+0

如果可以使用pdb(交互式),那麼應該可以使用模擬和自定義斷言(非交互式)。 – guettli

-1

也許這會爲你工作:

class SimpleTest(unittest.TestCase): 
    @override_settings(DEBUG=True) 
    def test_details(self): 
     client = Client() 
     response = client.get('/customer/details/') 
     self.assertEqual(response.status_code, 200, response.content) 

使用@override_settingsDEBUG=True將有就像你在DEBUG模式下運行實例的堆棧跟蹤。其次,爲了提供響應的內容,您需要使用print it或使用logging模塊記錄它,或者將其添加爲assert方法的消息。沒有調試器,一旦你assert,打印任何有用的東西(通常)爲時已晚。

您還可以配置logging並添加處理程序以將消息保存在內存中,並打印所有內容;無論是在自定義斷言方法還是在自定義測試運行器中。

+0

我認爲你的回答並不能解決我的問題。您的解決方案顯示視圖的結果(response.content)。我的需要:「我希望看到創建錯誤HTTPResponse對象的解釋器的堆棧跟蹤。」如果響應會產生內部服務器錯誤,則您的解決方案將起作用但是需要這樣的重定向和「拒絕權限」。 – guettli

1

我受到@Fush提出的解決方案的啓發,但我的代碼使用了assertRedirects,這是一種更長的方法,並且有點太多的代碼可以複製,而不會對自己感覺不好。

我花了一點時間搞清楚如何才能爲每個斷言調用super()並提出這個問題。我已經包含了2個示例斷言方法 - 它們基本上都是相同的。也許一些聰明的靈魂可以想到一些元類魔法,它對所有以「響應」作爲第一個參數的方法都是這樣。

from bs4 import BeautifulSoup 
from django.test.testcases import TestCase 


class ResponseTracebackTestCase(TestCase): 

    def _display_response_traceback(self, e, content): 
     soup = BeautifulSoup(content) 
     assert False, u'\n\nOriginal Traceback:\n\n{}'.format(
      soup.find("textarea", {"id": "traceback_area"}).text 
     ) 

    def assertRedirects(self, response, *args, **kwargs): 
     try: 
      super(ResponseTracebackTestCase, self).assertRedirects(response, *args, **kwargs) 
     except Exception as e: 
      self._display_response_traceback(e, response.content) 

    def assertContains(self, response, *args, **kwargs): 
     try: 
      super(ResponseTracebackTestCase, self).assertContains(response, *args, **kwargs) 
     except Exception as e: 
      self._display_response_traceback(e, response.content) 
+0

感謝您的分享! – guettli

-1

我子類Django的web客戶端,要得到這樣的:

使用

def test_foo(self): 
    ... 
    MyClient().get(url, assert_status=200) 

實施

from django.test import Client 

class MyClient(Client): 
    def generic(self, method, path, data='', 
       content_type='application/octet-stream', secure=False, 
       assert_status=None, 
       **extra): 
     if assert_status: 
      return self.assert_status(assert_status, super(MyClient, self).generic, method, path, data, content_type, secure, **extra) 
     return super(MyClient, self).generic(method, path, data, content_type, secure, **extra) 

    @classmethod 
    def assert_status(cls, status_code, method_pointer, *args, **kwargs): 
     assert hasattr(method_pointer, '__call__'), 'Method pointer needed, looks like the result of a method call: %r' % (method_pointer) 

     def new_init(self, *args, **kwargs): 
      orig_response_init(self, *args, **kwargs) 
      if not status_code == self.status_code: 
       raise HTTPResponseStatusCodeAssertionError('should=%s is=%s' % (status_code, self.status_code)) 
     def reraise_exception(*args, **kwargs): 
      raise 

     with mock.patch('django.core.handlers.base.BaseHandler.handle_uncaught_exception', reraise_exception): 
      with mock.patch.object(HttpResponseBase, '__init__', new_init): 
       return method_pointer(*args, **kwargs) 

結論

這導致在很長的異常,如果一個ht帶有錯誤狀態碼的tp響應被創建。如果你不怕長時間的例外,你會發現問題的根源非常快。這就是我想要的,我很高興。

學分

這是基於這個問題的其他答案。