I am using Django 1.8.4 and DRF 3.2.1 and when I run requests against the specified urls everything works fine, but when I run the tests with py.test the function update doesn't enter with the task_id param. But the url is fine.
Attached some code from urls.py and views.py and tests.py .. this is an excerpt of the code of course a lot of stuff is missing I just need eyes who can see if I am doing something wrong.
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
from rest_framework import viewsets
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)
And finally 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
By default when a new task is created it gets status 0, so I try to change the status to 2 and it fails, doing some debugging I found that is entering on the update function on RemoteTaskViewSet but is not getting the task_id.
I've followed lots of tutorials and changed back and forth the code and still having the same issue, luckily works in production but it worries to me that I cannot make it run test cases from this code.
The error output from py.test is this:
E assert 404 == 200
E + where 404 = <rest_framework.response.Response object at 0x7f9f465ae690>.status_code
I put a debugger into the update function, seems that task_id is None, but when I print request.stream the url is /api/remotetasks/1/
1 should be the task_id but isn't getting it, I was about to open a ticket on djangoproject but I think isn't a django bug since it works with external client, this must be something on my code, or anything else.
Update: If I use client
instead rf
and comment the line where I assign new_response
with the call of the method, and validate directly the against request.status_code it works!!!.
Something like this:
@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
Now the doubt is why it doesn't work in the previous way?
The issue (as noted in the updated) is in the request
assignment:
request = rf.put(url, data=params,
content_type='application/json')
new_response = RemoteTaskViewSet.as_view({'put': 'update'})(request)
assert new_response.status_code == 200
There really is no need to do the custom call to the view. That's already being done by the request assignment. It's not working in the old test because that's not how the view gets called when it's routed through the url routes.
Most importantly in the pervious code the request
object is not a request, it's the response
to the call. Using the old code I believe this would have worked as well:
@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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With