单元测试 Django JSON 视图

10 投票
2 回答
9732 浏览
提问于 2025-04-16 10:35

我正在为一些Django的json_view视图编写单元测试,但在将json字符串传递给视图时遇到了麻烦。我昨天发了一个相关的问题,关于如何从JS将json字符串传递给Django视图。问题在于,我在JS中只是直接传递了json字符串,而实际上我需要将这个字符串作为一个对象的属性来传递。因为没有这样做,导致字符串被当作查询字典的键来处理。现在我又遇到了类似的问题,不过这次是从Django的单元测试传递到Django视图。以下是我代码的简化版本,它产生了相同的结果。

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

而视图的样子是这样的

@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():
        ...

这是当测试运行时两个打印语句的输出。json字符串是

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

而查询字典看起来是这样的

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

我对JS和ajax完全是个新手,所以不用担心伤到我的自尊心,答案可能就在我眼前,随时都能跳出来咬我。

2 个回答

4

感谢@Eric_Fortin让我注意到这个头部设置,但这并没有解决我在使用'client.post'时遇到的查询字典格式错误的问题。当我把请求方式从POST改成GET,并使用了XMLHttpRequest的头部设置后,我的查询字典就正常了。以下是目前的解决方案:

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

这只是部分答案,因为这个请求是要修改服务器上的数据,应该使用POST而不是GET。

编辑:

这是我测试中最终的代码,它可以通过POST将一个JSON字符串传递到我的视图中:

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

现在从视图中打印出来显示查询字典格式正确。

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

我是在和我的一个同事一起调试时找到这个答案的,去掉了content_type 'json'后,查询字典的格式问题就解决了。被测试的视图并没有使用或调用'HttpRequest.is_ajax()',发送XMLHttpRequest的头部对我的问题没有影响,虽然加上这个头部是个好习惯,因为这个请求是一个ajax请求。

9

最后编辑

我最开始说在发帖时需要加上头信息 HTTP_X_REQUESTED_WITH='XMLHttpRequest',但现在在测试中发现这个说法不对。这个头信息对csrf中间件是必要的,但在测试中csrf是关闭的。不过,我还是觉得在测试中加上这个头信息是个好习惯,因为大多数JavaScript库在进行ajax请求时默认都会加上这个头信息。而且,如果有其他代码没有关闭csrf而使用了is_ajax方法,你就不用花几个小时去调试单元测试,去找头信息缺失的问题。

问题出在内容类型上,因为当Django接收到的内容类型不是 text/html 时,它就不会使用默认的post数据处理方式,而是会把你的数据格式化成查询字符串的样子,比如 type=book&author=JohnDoe

所以修正后的代码是:

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

这是我自己使用的方式:

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

用来做一些json-rpc。注意,由于我传递的内容类型与默认值不同,我的数据在post请求中是原样传递的。

撰写回答