Django或Django Rest Framework在测试时无法解析url param

Ang*_*uez 5 django pytest django-rest-framework

我正在使用Django 1.8.4和DRF 3.2.1,当我对指定的URL运行请求时一切正常,但是当我使用py.test运行测试时,函数更新不会进入task_id参数.但网址很好.

附上一些来自urls.py和views.py以及tests.py的代码..这是代码的摘录,当然很多东西都丢失了我只需要眼睛就能看出我做错了什么.

urls.py

from django.conf import settings
from django.conf.urls import include, patterns, url
from rest_framework import routers

from remotetask import views as rt_views

remotetask_detail = rt_views.RemoteTaskViewSet.as_view({'list': 'detail',
                                                       'put': 'update'})
remotetask_all = rt_views.RemoteTaskViewSet.as_view({'list': 'list'})

urlpatterns = patterns(
    '',
    url(r'^remotetasks/$', remotetask_all, name='api-remotetask-all'),
    url(r'^remotetasks/(?P<task_id>\d+)/$', remotetask_detail,
        name='api-remotetask-detail'),
    url(r'^api-auth/', include('rest_framework.urls',
                               namespace='rest_framework')),
)
Run Code Online (Sandbox Code Playgroud)

views.py

from django.shortcuts import get_object_or_404

from rest_framework import generics
from rest_framework import status
from rest_framework.response import Response

from remotetask.models import RemoteTask                                                                                                        
from remotetask.serializers import RemoteTaskSerializer
Run Code Online (Sandbox Code Playgroud)

来自rest_framework导入视图集

class RemoteTaskViewSet(viewsets.ViewSet):                                                                                        
    queryset = RemoteTask.objects.all()                                                                                                         
    serializer_class = RemoteTaskSerializer                                                                                                     

    def detail(self, request, task_id=None):
        task = get_object_or_404(RemoteTask, pk=task_id)
        serializer = RemoteTaskSerializer(task)

        return Response(serializer.data)


    def update(self, request, task_id=None):
        task = get_object_or_404(RemoteTask, pk=task_id)
        new_status = request.data.get('status')

        status_changed = task.change_status(new_status, stdout, stderr)

        if status_changed:
            response_status = status.HTTP_201_CREATED
        else:
            response_status = status.HTTP_400_BAD_REQUEST

        serializer = RemoteTaskSerializer(task)

        return Response(serializer.data, status=response_status)
Run Code Online (Sandbox Code Playgroud)

最后是test_views.py

import pytest

from django.core.urlresolvers import reverse

from remotetask.factories import RemoteTaskFactory
from remotetask.models import RemoteTask
from remotetask.views import RemoteTaskViewSet

import json

@pytest.fixture()
@pytest.mark.django_db
def create_remotetask():
    remotetask = RemoteTaskFactory.create()
    return remotetask

@pytest.fixture()
@pytest.mark.django_db()
def clean_remotetask():
    RemoteTask.objects.all().delete()


@pytest.fixture()
def rq_remotetasklist(rf):
    url = reverse('api-remotetask-all')
    request = rf.get(url)
    response = RemoteTaskViewSet.as_view({'list': 'list'})(request)
    return response

@pytest.mark.usefixtures('clean_remotetask', 'create_remotetask')
@pytest.mark.django_db
def test_remotetask_changestatus(rq_remotetasklist, rf):
    response = rq_remotetasklist
    result = response.data.get('results')
    id_to_work = result[0]['id']
    rt = RemoteTask.objects.get(pk=id_to_work)
    assert rt.status == 0
    # new request
    url = reverse('api-remotetask-detail', kwargs={'task_id':id_to_work})
    params = json.dumps({'status': 2, 'stdout': 'test', 'stderr': 'ok'})

    request = rf.put(url, data=params,
                     content_type='application/json')

    new_response = RemoteTaskViewSet.as_view({'put': 'update'})(request)

    assert new_response.status_code == 200
Run Code Online (Sandbox Code Playgroud)

默认情况下,当创建新任务时,它获取状态0,因此我尝试将状态更改为2并且失败,执行一些调试,我发现它正在RemoteTaskViewSet上的更新函数中进入,但没有获得task_id.

我已经遵循了很多教程并且来回更改了代码并且仍然遇到了相同的问题,幸运的是在生产中工作但是我担心我不能让它从这段代码中运行测试用例.

py.test的错误输出是这样的:

E       assert 404 == 200
E        +  where 404 = <rest_framework.response.Response object at 0x7f9f465ae690>.status_code
Run Code Online (Sandbox Code Playgroud)

我把一个调试器放到更新函数中,似乎task_id是None,但是当我打印request.stream时url是/api/remotetasks/1/1应该是task_id但是没有得到它,我打算在djangoproject上打开一张票,但我认为是这是一个django错误,因为它适用于外部客户端,这必须是我的代码或其他任何东西.

更新:如果我client改为使用方法调用并使用方法调用rf注释我指定的行new_response,并直接验证反对request.status_code它可以工作!!!

像这样的东西:

@pytest.mark.usefixtures('clean_remotetask', 'create_remotetask')
@pytest.mark.django_db
def test_remotetask_changestatus(rq_remotetasklist, client):
    response = rq_remotetasklist
    result = response.data.get('results')
    id_to_work = result[0]['id']
    rt = RemoteTask.objects.get(pk=id_to_work)
    assert rt.status == 0
    # new request
    url = reverse('api-remotetask-detail', kwargs={'task_id': id_to_work})
    params = json.dumps({'status': 2, 'stdout': 'test', 'stderr': 'ok'})

    request = client.put(url, data=params,
                         content_type='application/json')

    assert request.status_code == 201
Run Code Online (Sandbox Code Playgroud)

现在有疑问是为什么它不能用以前的方式工作?

Gar*_*ley 1

问题(如更新中所述)位于作业中request

request = rf.put(url, data=params,
                 content_type='application/json')

new_response = RemoteTaskViewSet.as_view({'put': 'update'})(request)

assert new_response.status_code == 200
Run Code Online (Sandbox Code Playgroud)

实际上没有必要对视图进行自定义调用。这已经通过请求分配完成了。它在旧测试中不起作用,因为这不是通过 url 路由路由时调用视图的方式。

最重要的是,在前面的代码中request,对象不是请求,而是response调用。使用旧代码我相信这也会有效:

@pytest.mark.usefixtures('clean_remotetask', 'create_remotetask')
@pytest.mark.django_db
def test_remotetask_changestatus(rq_remotetasklist, rf):
    response = rq_remotetasklist
    result = response.data.get('results')
    id_to_work = result[0]['id']
    rt = RemoteTask.objects.get(pk=id_to_work)
    assert rt.status == 0
    # new request
    url = reverse('api-remotetask-detail', kwargs={'task_id':id_to_work})
    params = json.dumps({'status': 2, 'stdout': 'test', 'stderr': 'ok'})

    response = rf.put(url, data=params,
                     content_type='application/json')

    assert response.status_code == 200
Run Code Online (Sandbox Code Playgroud)