2015-09-03 14 views
5

Sto utilizzando Django 1.8.4 e DRF 3.2.1 e quando eseguo richieste contro gli URL specificati tutto funziona correttamente, ma quando eseguo i test con py. testare l'aggiornamento della funzione non entra con il parametro task_id. Ma l'url va bene.Django o Django Rest Framework non può risolvere il parametro url durante il test

In allegato un codice da urls.py e views.py e tests.py .. questo è un estratto del codice, ovviamente manca un sacco di cose Ho solo bisogno di occhi che possano vedere se sto facendo qualcosa di sbagliato.

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

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 

da rest_framework viewsets importazione

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) 

E infine 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 

Per impostazione predefinita, quando viene creata una nuova attività, ottiene lo stato 0, quindi provo a cambiare lo stato su 2 e non riesce, eseguendo un po 'di debug che ho trovato che sta entrando nella funzione di aggiornamento su RemoteTaskViewSet ma non sta ottenendo il task_id.

Ho seguito un sacco di esercitazioni e ho cambiato il codice e continuo ad avere lo stesso problema, fortunatamente funziona in produzione ma mi preoccupa che non riesco a farlo eseguire test case da questo codice.

L'uscita di errore da py.test è questo:

E  assert 404 == 200 
E  + where 404 = <rest_framework.response.Response object at 0x7f9f465ae690>.status_code 

ho messo un debugger nella funzione di aggiornamento, sembra che task_id è Nessuno, ma quando stampo request.stream l'URL è /api/remotetasks/1/ 1 dovrebbe essere il task_id ma non lo capisce, stavo per aprire un ticket su djangoproject ma penso che non sia un bug django visto che funziona con client esterni, questo deve essere qualcosa sul mio codice, o qualsiasi altra cosa.

Aggiornamento: Se uso client invece rf e commentare la linea dove assegno new_response con la chiamata del metodo, e convalidare direttamente l'request.status_code contro di esso funziona !!!.

Qualcosa di simile a questo:

@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 

Ora il dubbio è il motivo per cui non funziona nel modo precedente?

+0

Cosa succede se si rimuovono tutti i test diversi da quello che non riesce? Se si inserisce una dichiarazione pdb e si ispeziona l'oggetto risposta cosa ha? Anche perché il {'put': 'update'} nella chiamata as view? –

+0

Nell'esempio c'è solo un test, l'ho fatto e il test continua a fallire. Intendi la new_response che ha un oggetto rest_framewor.response.Response. Perché l'aggiornamento messo? .. beh questo è un materiale DRF di cui ho bisogno dal momento che RemoteTaskViewSet è figlio di viewset.ViewSet –

risposta

1

Il problema (come indicato nella aggiornata) è nell'assegnazione 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 

Non c'è davvero alcun bisogno di fare la chiamata personalizzata per la vista. Questo è già stato fatto dall'assegnazione della richiesta. Non funziona nel vecchio test perché non è il modo in cui la vista viene chiamata quando viene instradata attraverso le rotte URL.

Soprattutto nel prefisso codice l'oggetto request non è una richiesta, è il response della chiamata.Utilizzando il vecchio codice, credo che avrebbe funzionato anche:

@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 
+0

Ciao Gary, grazie per l'aiuto, ma quando stavo iniziando a fare i test ho provato in quel modo, sembra che ogni volta che usi RequestFactory (rf) tu sono costretti a fare la vista della chiamata personalizzata, la risposta non ha status_code o valore, ad esempio, ma grazie per l'aiuto! –

+0

Hai ragione, non ho notato che stava usando la factory richiesta. https://docs.djangoproject.com/en/1.8/topics/testing/advanced/#the-request-factory. RequestFactory deve essere utilizzato quando non si desidera/deve utilizzare le chiamate HTTP. Dato che questa è un'API, preferirei usare client come mostra il tuo aggiornamento. –