Search code examples
pythondjangopostpython-unittestdjango-unittest

Django self.client.post does not send data to view with decorator


I have a simple test case which checks that POST request with valid data returns an HTML response with 200 status code:

class PostRequestTestCase(TestCase):
    def test_valid_post_request(self):
        response = self.client.post('/foo/', data={'text': 'bar'})
        self.assertEqual(response.status_code, 200)

Here is the view foo which is triggered for that request:

logger = logging.getLogger(__name__)

# decorator to trace enter and exit events
enter_exit_tracer = enter_exit_Tracer(logger)

@enter_exit_tracer
def foo(request):  
    if request.method == 'POST':
        #
        print('request.POST:', request.POST)
        # 
        # some stuff

where @enter_exit_tracer is a decorator to trace entering/exiting a function:

def enter_exit_Tracer(logger):
    def middle(f): 
        def inner(*args, **kwargs):
            session_string = args[-1] if 'session_key' in args[-1] else '[]'
            logger.debug('%s Enter %s.', session_string, f.__name__)
            result = f(*args, **kwargs)
            logger.debug('%s Exit %s.',  session_string, f.__name__)
            return result
        return inner
    return middle

It turns out that when I add this decorator to foo function, then the POST data sent via self.client.post are actually not passed to foo. They are missing in the test request - so my test fails:

DEBUG: [] Enter foo.
request.POST: <QueryDict: {}>
ERROR: Invalid form
F
======================================================================
FAIL: test_valid_post_request (textstat.tests.PostRequestTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/user/Pro/myapp/tests.py", line 111, in test_valid_post_request
    self.assertEqual(response.status_code, 200)
AssertionError: 400 != 200

----------------------------------------------------------------------
Ran 1 test in 0.018s

FAILED (failures=1)

We see request.POST: <QueryDict: {}> and eventually this leads to ERROR: Invalid form.

At the same time when I do the similar POST request but via a web browser everything works fine - the form gets populated with data, the web page is rendered ok and @enter_exit_tracer does its logging as expected.

If I comment out @enter_exit_tracer decorator then everything works also ok and the test is passed:

request.POST: <QueryDict: {'text': ['bar']}>
.
----------------------------------------------------------------------
Ran 1 test in 0.007s

OK

The questions:

  • why the request.POST data are not passed to the view with the decorator in case of self.client.post request?
  • if anything wrong with the decorator - why then the web request works ok?
  • is it possible to keep the decorator and makeself.client.post pass the data to the view?

Solution

  • It turns out that the decorator has a TypeError error in this line:

    session_string = args[-1] if 'session_key' in args[-1] else '[]'
    

    So when args[-1] is not str then this should not work. Fixing it with str(args[-1]) resolves the whole issue with missing POST data.

    It is weird though that self.client.post does not raise any TypeError and session_string was somehow calculated. Instead of this POST data were not sent to the view without any errors or hint warnings. POST data were just disappeared. At the same time the same code worked ok when POST request was sent from web browser, so the error remained unnoticed.