此模式来自django docs:
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 :(得分:2)
如果断言失败而没有调试
,如何查看回溯
如果断言失败,则没有回溯。 client.get()
没有失败,只是返回了与您预期不同的响应。
您可以使用pdb逐步完成client.get()
调用,并查看其返回意外回复的原因。
答案 1 :(得分:2)
我认为可以通过创建一个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)
答案 2 :(得分: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)
答案 3 :(得分:0)
也许这对你有用:
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_settings
拥有DEBUG=True
将拥有堆栈跟踪,就像您在DEBUG
模式下运行实例一样。
其次,为了提供回复的内容,您需要print
或使用logging
模块进行记录,或将其添加为assert
的消息方法。没有调试器,一旦assert
,打印任何有用的东西(通常)都为时已晚。
您还可以配置logging
并添加处理程序以将消息保存在内存中,并打印所有内容;无论是在自定义断言方法还是在自定义测试运行器中。
答案 4 :(得分:-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)
如果创建了具有错误状态代码的http响应,则会导致长异常。如果你不害怕长期例外,你会非常快速地看到问题的根源。这就是我想要的,我很高兴。
这是基于这个问题的其他答案。