Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask unit testing: Getting the response's redirect location

I have a Flask based webapp that occasionally creates new documents with new random keys when posting to their parent document in a certain way. The new key gets in the parent's data structure, the updated parent gets temporarily stored in the session and on successfully saving the child document, the stored parent gets pulled out of the session and stored alongside, in order to link the two together. This is done for certain kinds of relations where one wants to have an inherent order between the keys, so the keys are stored as a list on the parent.

Now, the problem comes in when I want to unit test this using the Werkzeug provided unit test client. Doing a

 ret = self.test_client.post(
    request_path,
    data=data,
    follow_redirects=True
)

in the test case object will successfully redirect to the child document with the new key - but I don't know where to retrieve this new key inside the unit tests. I can't find an attribute on top of the return value that would indicate, where it has been redirected to. dir(ret) gives me

 ['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__enter__', '__exit__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_ensure_sequence', '_get_mimetype_params', '_on_close', '_status', '_status_code', 'accept_ranges', 'add_etag', 'age', 'allow', 'autocorrect_location_header', 'automatically_set_content_length', 'cache_control', 'calculate_content_length', 'call_on_close', 'charset', 'close', 'content_encoding', 'content_language', 'content_length', 'content_location', 'content_md5', 'content_range', 'content_type', 'data', 'date', 'default_mimetype', 'default_status', 'delete_cookie', 'direct_passthrough', 'expires', 'force_type', 'freeze', 'from_app', 'get_app_iter', 'get_data', 'get_etag', 'get_wsgi_headers', 'get_wsgi_response', 'headers', 'implicit_sequence_conversion', 'is_sequence', 'is_streamed', 'iter_encoded', 'last_modified', 'location', 'make_conditional', 'make_sequence', 'mimetype', 'mimetype_params', 'response', 'retry_after', 'set_cookie', 'set_data', 'set_etag', 'status', 'status_code', 'stream', 'vary', 'www_authenticate']

Of those, headers and location look promising - however location isn't set and headers doesn't contain it either.

How can I get the redirected location from the response? Do I really have to parse the child's key from the response body? Isn't there a better way?

like image 445
Michel Müller Avatar asked Mar 21 '14 18:03

Michel Müller


2 Answers

Following on from your own answer, depending on your personal preference of unit test style so feel free to ignore, you may prefer something like the following suggestion to simplify and improve clarity and readability of the unit test:

# Python 3
from urllib.parse import urlparse
# Python 2
from urlparse import urlparse

response = self.test_client.post(
    request_path,
    data=data,
    follow_redirects=False
)
expectedPath = '/'
self.assertEqual(response.status_code, 302)
self.assertEqual(urlparse(response.location).path, expectedPath)
like image 131
Pete Watts Avatar answered Sep 30 '22 07:09

Pete Watts


@bwbrowning has provided the correct hint - doing a post with follow_redirects=False the return value has the location attribute set - which is the complete request path including parameters.

edit: hint - there is a slight gotcha when doing the test_client.get(..) - the path parameter needs to be a relative path, while ret.location return the full path. so, what I did was

child_path_with_parameters = rv.location.split('http://localhost')[1]
child_path = child_path_with_parameters.split('?')[0]
ret = self.test_client.get(child_path_with_parameters)

(the child_path is used later in order to post to the child)

like image 41
Michel Müller Avatar answered Sep 30 '22 07:09

Michel Müller