Client patch gives 400 error in django testing - django

So basically when i try to test my patch view it doesn't patch at all as i get a 400 error. However, when i call the same patch view outside the APITestCase, while running the test server, it does work.
what i've tried:
patch_data = json.dumps({'first_name': 'test999'})
format='json'
At APITestCase:
def test_patch_update_data(self):
self.client.force_authenticate(user=self.user)
self.assertEqual(self.user.first_name, 'Robert')
patch_data = {'first_name': 'test999'}
response = self.client.patch('http://testserver/api/patch/{0}/'.format(
self.user.id), patch_data, format='json')
self.assertEqual(response.status_code, 200) # ERROR:GIVES 400!
self.assertEqual(self.user.first_name, 'test999')
At views.py
class UsuariosUpdatePatchAPIView(UpdateAPIView):
queryset = Usuarios.objects.all()
serializer_class = UsuariosUpdateSerializer
permission_classes = [IsAuthenticated, IsSelfUser]
The error:
self.assertEqual(response.status_code, 200)
AssertionError: 400 != 200
Thanks in advance! :)

How silly i am:
As stated in the comments, response.data gives very useful information. It turned out to be that the field fist_name only accepted letters! So the solution was changing test999 to something without numbers!

Related

django testing: client.get returns MultipleObjectsReturned error

When I run the following test, I want client.get() to return a list of examples. It does so correctly, but it throws this error:
MultipleObjectsReturned: get() returned more than one Example -- it returned 8!
How do I deactivate this error?
def test_read_all_examples(self):
# get all examples
url = reverse('example')
admin = User.objects.get(username='admin')
client = APIClient()
client.force_authenticate(user=admin)
response = client.get(url, format='json')
print("GET - ExampleTests Response:", response.data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
I am not sure how you got this problem, but the get call threw the exception was User.objects.get not client.get. I would check your database tables for multiple accounts with the username of admin.
Adding: it’s typically calls to ModelManager.get that throw DoesNotExist and MultipleObjectsReturned. https://docs.djangoproject.com/en/1.10/ref/exceptions/

django and python requests - getting a 403 on a post request

I am using requests to log into my Django site for testing (and yes, I know about the Django TestClient, but I need plain http here). I can log in and, as long as I do get requests, everything is OK.
When I try to use post instead, I get a 403 from the csrf middleware. I've worked around that for now by using a #crsf_exempt on my view, but would prefer a longer term solution.
This is my code:
with requests.Session() as ses:
try:
data = {
'username': self.username,
'password': self.password,
}
ses.get(login_url)
try:
csrftoken = ses.cookies["csrftoken"]
except Exception, e:
raise
data.update(csrfmiddlewaretoken=csrftoken)
_login_response = ses.post(login_url, data=data)
logger.info("ses.cookies:%s" % (ses.cookies))
assert 200 <= _login_response.status_code < 300, "_login_response.status_code:%s" % (_login_response.status_code)
response = ses.post(
full_url,
data=data,
)
return self._process_response(response)
The login works fine, and I can see the csrf token here.
INFO:tests.helper_fetch:ses.cookies:<RequestsCookieJar[<Cookie csrftoken=TmM97gnNHs4YCgQPzfNztrAWY3KcysAg for localhost.local/>, <Cookie sessionid=kj6wfmta
However, the middleware sees cookies as empty.
INFO:django.middleware.csrf:request.COOKIES:{}
I've added the logging code to it:
def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(request, 'csrf_processing_done', False):
return None
try:
csrf_token = _sanitize_token(
request.COOKIES[settings.CSRF_COOKIE_NAME])
# Use same token next time
request.META['CSRF_COOKIE'] = csrf_token
except KeyError:
# import pdb
# pdb.set_trace()
import logging
logger = logging.getLogger(__name__)
logger.info("request.COOKIES:%s" % (request.COOKIES))
Am I missing something with way I call request's session.post? I tried adding cookie to it, made no difference. But I can totally see why crsf middleware is bugging out. I thought the cookies were part of the session, so why are they missing in my second post?
response = ses.post(
self.res.full_url,
data=data,
cookies=ses.cookies,
)
This variation, inspired by How to send cookies in a post request with the Python Requests library?, also did not result in anything being passed to csrf middleware:
response = ses.post(
self.res.full_url,
data=data,
cookies=dict(csrftoken=csrftoken),
)
For subsequent requests after the login, try supplying it as header X-CSRFToken instead.
The following worked for me:
with requests.Session() as sesssion:
response = session.get(login_url)
response.raise_for_status() # raises HTTPError if: 400 <= status_code < 600
csrf = session.cookies['csrftoken']
data = {
'username': self.username,
'password': self.password,
'csrfmiddlewaretoken': csrf
}
response = session.post(login_url, data=data)
response.raise_for_status()
headers = {'X-CSRFToken': csrf, 'Referer': url}
response = session.post('another_url', data={}, headers=headers)
response.raise_for_status()
return response # At this point we probably made it
Docs reference: https://docs.djangoproject.com/en/dev/ref/csrf/#csrf-ajax
You could also try to use this decorator on your view, instead of the csrf_exempt. I tried to reproduce your issue, and this worked as well for me.
from django.views.decorators.csrf import ensure_csrf_cookie`
#ensure_csrf_cookie
def your_login_view(request):
# your view code

Using nested objects with Django Rest Framework and unit tests

I wrote several unit tests on my Django Rest Framework endpoints without any trouble, until I tried to pass nested object in a POST request:
class BookTestCase(APIVersion, APITestCase):
def setUp(self):
self.url = self.reverse_with_get_params('book')
self.user = CustomerFactory.create().user
self.base_data = {"foo": "bar",
"credit_card": {"card_number": "1234567812345678",
"expiration_date": "1116",
"security_code": "359"},
"foo2": "bar2"}
def test_book(self):
add_token_to_user(self.user, self.client)
response = self.client.post(self.url, self.base_data)
self.assertEqual(response.status_code, 200)
Then, runing the related web service with a pdb.set_trace() at the very beginning, here is the content of request.DATA:
<QueryDict: {u'foo': [u'bar'],
u'credit_card': [u'expiration_date', u'security_code', u'card_number'],
u'foo2': [u'bar2']}>
As you can see, every level1 object is correctly filled, but credit card content has disapeared.
Any idea? Thanks!
Note: Django 1.6 / Rest Framework 2
You have to change to format of your post call. Try format='json'
response = self.client.post(self.url, self.base_data, format='json')

Django check for error messages in context

I have a simple test:
def test_project_info_form_post_submission(self):
"""
Test if project info form can be submitted via post.
"""
# set up our POST data
post_data = {
'zipcode': '90210',
'module': self.module1.pk,
'model': self.model1.pk,
'orientation': 1,
'tilt': 1,
'rails_direction': 1,
}
...
response = self.client.post(reverse(url), post_data)
self.assertEqual(response.status_code, 302)
# test empty form
response = self.client.post(reverse(url))
self.assertEqual(response.status_code, 200)
#! test for general form error message
# now test invalid responses
post_data['zipcode'] = 'abcdefg'
response = self.client.post(reverse(url), post_data)
self.assertEqual(response.status_code, 200)
#! test for specific error message associated with zipcode
So the lines I am having trouble with are marked with shebangs. I know I should have messages in the context variable but can't seem to figure out the right ones to use.
You can test if the template contains your message in your TestCase with assertContains.

Django testing - Taking the initial values and feeding them back

I have a basic model which references both ForeignKeys and ManyToMany objects. In "edit" testing where you are taking the id of a view and making changes to it I ran into a problem and I'm curious if anyone else figured out a cleaner workaround. I found this post which started me down the right path
client = Client()
response = client.get(reverse("floorplan_update", kwargs={'pk': floorplan.id}))
data = response.context['form'].initial
# Ideally you should be able to do this..
response = client.post(reverse("floorplan_update", kwargs={'pk': floorplan.id}),
data=data, follow=True)
But you can't do this. In cases where you have FK's or M2M's you need to first do this ugliness...
client = Client()
response = client.get(reverse("floorplan_update", kwargs={'pk': floorplan.id}))
data = response.context['form'].initial
# Ugliness ensues..
data['document'] = open(__file__)
data['company']= data['company'].id
data['target']= data['target'].id
# Only now can you post..
response = client.post(reverse("floorplan_update", kwargs={'pk': floorplan.id}),
data=data, follow=True)
Has anyone else ran into this or is there a better way to deal with this?
Not sure, but you can try this instead:
data = response.context['form'].instance.__dict__