guettli guettli - 11 days ago 6
Python Question

Django Testing: See traceback where wrong Response gets created

This pattern is from the django docs:

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


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

If the test fails, the error message does not help very much. For example the status_code is 302. I see
302 != 200
.

The question is now: Where does the wrong HTTPResponse get created?

I would like to see the stacktrace of the interpreter where the wrong HTTPResponse object get created.

I read the docs for the assertions of django but found no matching method.

Update

Please don't explain the reason for a http response with status 302. This is a general question: How to see the traceback immediate if the assertion fails without debugging?

Background

I want a short CI-error to error fixed path. If I see
302 != 200
in the output of our continuous intergration server, I don't know what's wrong. If I see the traceback where the wrong HTTPResponse gets created, then I have a much better overview. Maybe I even don't need to debug this myself :-) Maybe I see that the wrong response was created in a module where a team worker is currently working on ...

Answer

I think it could be achieved by creating a TestCase subclass that monkeypatches django.http.response.HttpResponseBase.__init__() to record a stack trace and store it on the Response object, then writing an assertResponseCodeEquals(response, status_code=200) method that prints the stored stack trace on failure to show where the Response was created.

I could actually really use a solution for this myself, and might look at implementing it.

Update: Here's a v1 implementation, which could use some refinement (eg only printing relevant lines of the stack trace).

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)
Comments