10

I'm trying to write some unit tests for some Django json_view views and I'm having trouble passing the json_string to the view. I posted a related question yesterday about passing a json string to a Django view from the JS, the issue was that in my JS I was just passing the json string where I needed to be passing the string as the attribute of an object, because I was failing to do this the string was being taken as the key for the resulting query dict. I'm having a similar problem again except that this time it is form a Django unit test to the Django View. Here is a simplified version of my code which produces the same result.

class MyTestCase(TestCase):
    def setUp(self):
        self.u = User.objects.create_user('test','test','test')
        self.u.is_active = True
        self.u.save()
        self.client.login(username='test',password='test')

    def test_create_object_from_form(self):
        """Test the creation of the Instance from the form data."""
        import json
        json_string json.dumps({'resource':{'type':'book','author':'John Doe'}})
        print(json_string)
        response = self.client.post(reverse('ajax_view'),
                                    {'form':json_string},'json')
        self.assetNotContains(response,'error')

and the view looks like this

@json_view
def ajax_view(request):
    """Process the incoming form data."""
    if request.method == 'POST':
        print(request.POST)
        form_data = json.loads(request.POST['form'])
        resource_data = form_data['resource']
        form = MyUserForm(resource_data)

        if form.is_valid():
        ...

Here is what the two print statements produce when the test is run. The json_string is

{"resource": {"type": "book", "author": "John Doe"}}

and the query dict looks like

<QueryDict: {u'{\'form\': \'{"resource": {"type": "book", "author": "John Doe"}}\'}': [u'']}>

I'm total newbie with JS and ajax, so don't worry about hurting my pride, the answer is probably so close it could jump up and bite me.

Serjik
  • 10,543
  • 8
  • 61
  • 70
snarkyname77
  • 1,154
  • 1
  • 10
  • 23

2 Answers2

9

Final edit

I originally stated that header HTTP_X_REQUESTED_WITH='XMLHttpRequest' was necessary in the post call but this is currently false while in tests. This header is necessary for the csrf middleware but csrf is disabled in tests. However, I still believe it is a good practice to put in test even if middleware disables csrf since most javascript library already pass this header by default when doing ajax. Also, if another piece of code that is not disabled ever use the is_ajax method, you won't need to debug your unittest for hours to figure out that the header was missing.

The problem is with the content-type because when django gets a value in there that is different than text/html, it doesn't use the default post data handling which is to format your data like in a query: type=book&author=JohnDoe for example.

Then the fixed code is:

response = self.client.post(reverse('ajax_view'),
                            {'form':json_string}, 
                            HTTP_X_REQUESTED_WITH='XMLHttpRequest')

Here's how I'm using it myself:

post_data = { 
    "jsonrpc" : "2.0", "method": method, "params" : params, "id" : id }
return client.post('/api/json/', 
                    json.dumps(post_data), "text/json",            
                    HTTP_X_REQUESTED_WITH='XMLHttpRequest')

to do some json-rpc. Notice that since I pass a different content-type than the default value, my data is passed as is in the post request.

Eric Fortin
  • 7,533
  • 2
  • 25
  • 33
  • 1
    Even with the addition of the header 'XMLHttpRequest' I still get the JSON string in the key position of the query dictionary. – snarkyname77 Jan 25 '11 at 15:04
  • Remove the second example of how you are using client.post and I'll mark this as the answer, the second example contains the issue that I created the post to resolve. – snarkyname77 Jan 28 '11 at 17:40
  • Hum sorry, the second example comes from my code base and works perfectly with my json decorator. Keep in mind I'm doing json-rpc which is different than your use-case. – Eric Fortin Jan 28 '11 at 18:57
  • If it helps anyone else, I was working on a django-piston API and found that it, by default declines, 'text/json' but does accept ' application/json – Steve Jalim Aug 09 '11 at 15:06
4

Thank you to @Eric_Fortin for turning me on to the header, it does not however resolve my issue with the malformed query dictionary using 'client.post'. Once I made the change from POST to GET with the XMLHttpRequest header my query dictionary straitened itself out. Here is the current solution:

response = self.client.get(reverse('ajax_view'),
                           {'form':json_string},'json',
                           HTTP_X_REQUESTED_WITH='XMLHttpRequest')

this is only a partial answer since this request is going to change data on the server and should be POST not a GET.

Edit:

Here is the final code in my test that works for passing a JSON string via POST to my view:

response = self.client.post(reverse('ajax_view'),
                            {'form':json.dumps(json_dict)})

Now printing from the view shows that the query dictionary is well formed.

<QueryDict: {u'form': [u'{"resource": {"status": "reviewed", "name": "Resource Test", "description": "Unit Test"}}']}>

I found the answer while tinkering with one of my co-workers, removing the content_type 'json' fixed the malformed query dictionary. The view that is being tested does not make use of or call the 'HttpRequest.is_ajax()', sending the header XMLHttpRequest' has no impact on my issue, though including the header would constitute good-form since this post is an ajax request.

snarkyname77
  • 1,154
  • 1
  • 10
  • 23
  • That's wrong, it also works with post. See my edited response. – Eric Fortin Jan 25 '11 at 15:56
  • Still, even with changing the code so that the json string is supplied directly as the 'data' argument to 'post' method my query dictionary still comes out mangled in my 'view' with the json string in the key. Changing the method from 'post' to 'get' and leaving the json_string referenced as the value of a dictionary, the query dictionary is structured properly. I stand by my response. – snarkyname77 Jan 25 '11 at 16:17
  • Edited my answer to explain the problem and detail the solution. – Eric Fortin Jan 26 '11 at 02:55
  • I acknowledge the sending of the header as good-form since this post is an ajax request, though the inclusion of the header has no impact on my issue which is the malformation of the query dictionary. Supplying only the json_string as the second argument to the 'client.post' method results in a malformed query dictionary. The query dictionary produced by the 'client.post' is also malformed when the content_type is set to 'text/json' or just 'json'. – snarkyname77 Jan 26 '11 at 14:19