How can I write tests that populates raw_post_data and request.FILES['myfile'] - django

I have something like this:
def upload_something(request):
data = {}
if request.FILES:
raw_file = request.FILES['myfile'].read()
else:
raw_file = request.raw_post_data
I can't seem to be able to write a unit-test that populates raw_post_data, how would I go about doing that? I basically just want to send an image file. I'm trying to create a test case for when I read raw_post_data and it errors with:
You cannot access raw_post_data after reading from request's data stream

I'm assuming you have figured this out by now, but as the answers are almost out of date with the deprecation of raw_post_data I thought i'd post.
def test_xml_payload(self):
data = '<?xml version="1.0" encoding="UTF-8"?><blah></blah>'
response = self.client.post(reverse('my_url'),
data=data,
content_type='application/xml')
def my_view(request):
xml = request.body

You can use mocking. Some examples available here and in docs here
Updated
Kit, I think it's very depends on your test case. But in general you shouldn't use raw_post_data directly. Instead it's have to be patched like in example below:
from mock import Mock, MagicMock
class SomeTestCase(TestCase):
def testRawPostData(self):
...
request = Mock(spec=request)
request.raw_post_data = 'myrawdata'
print request.raw_post_data # prints 'myrawdata'
file_mock = MagicMock(spec=file)
file_mock.read.return_value = 'myfiledata'
request.FILES = {'myfile': file_mock}
print request.FILES['myfile'].read() # prints 'myfiledata'

The error message the interpreter is giving is correct. After you access the POST data via if request.FILES, you can no longer access the raw_post_data. If in your actual code (not the tests) you hit that line, it would error with the same message. Basically, you need two separate views for form-based POSTS and direct file POSTS.

I took this listing here
c = Client()
f = open('wishlist.doc')
c.post('/customers/wishes/', {'name': 'fred', 'attachment': f})
f.close()
Client is a special class for testing your views. This is the example of posting files to your view. It's part of Django testing framework.

Related

DRF CamelCase parser not working on actions

In my project, i have added the Django REST Framework JSON CamelCase package according to the documentation.
I'd like to implement a method like this one:
#action(detail=False, methods=['patch'], permission_classes=[IsAuthenticated])
def something(self, request):
body = json.loads(request.body)
print(body)
o = self.get_queryset().get(pk=body['id'])
s = Serializer(o, data=body)
if s.is_valid():
s.save()
return Response()
However, this method prints the variables I sent in camelCase, not in snake_case, so the object can't save. What should I do to solve this?
Note: I'd still like an answer as to why the above didn't work.
I've found a workaround by just using the parser directly in the method. Instead of using body = json.loads(request.body), I use body = CamelCaseJSONParser().parse(request.stream).

Get data from JsonResponse in django

I wanted to know how to get data from a JsonResponse in django. I made a JsonResponse that works like this
def pfmdetail(rsid):
snpid = parseSet(rsid)
if not snpid:
return HttpResponse(status=404)
try:
data = SnpsPfm.objects.values('start', 'strand', 'type', 'scoreref', 'scorealt',
rsid=F('snpid__rsid'), pfm_name=F('pfmid__name')).filter(snpid=snpid[0])
except SnpsPfm.DoesNotExist:
return HttpResponse(status=404)
serializer = SnpsPfmSerializer(data, many=True)
return JsonResponse(serializer.data, safe=False)
and then I call directly the method like this
def pfmTable(qset,detail):
source = pfmdetail(detail)
print(source)
df = pd.read_json(source)
but it gives me an error. I know it's wrong because with the print it returns the status of the response which is 200 so I suppose that the response is fine but how can I access the data inside the response? I tried import json to do json.load but with no success. I even tried the methods of QueryDict but stil I can't acess to the content I'm interested
P.S. I know that data contains something because if i display the jsonresponse on the browser i can see the JSON
As you can see here: https://docs.djangoproject.com/en/2.2/ref/request-response/#jsonresponse-objects.
JsonResponse object holds json in its content attribute.
So to access it try this:
df = pd.read_json(source.content)
Or to see it printed do:
print(source.content)
If you aren't using pandas, then you should process the content attribute of the JSONResponse object like this:
r = json.loads(source.decode())
I got the answer here: How to parse binary string to dict ?

Only lists and tuples may be used in a list field Validation Error

Hi I am implementing test cases for my models.
I am using Mongoengine0.9.0 + Django 1.8
My models.py
class Project(Document):
# commented waiting for org-group to get finalize
project_name = StringField()
org_group = ListField(ReferenceField(OrganizationGroup, required=False))
My Serializers.py
class ProjectSerializer(DocumentSerializer):
class Meta:
model = Project
depth = 1
test.py file
def setUp(self):
# Every test needs access to the request factory.
self.factory = RequestFactory()
self.user = User.objects.create_user(
username='jacob', email='jacob#jacob.com', password='top_secret')
def test_post_put_project(self):
"""
Ensure we can create new clients in mongo database.
"""
org_group = str((test_utility.create_organization_group(self)).id)
url = '/project-management/project/'
data = {
"project_name": "googer",
"org_group": [org_group],
}
##import pdb; pdb.set_trace()
factory = APIRequestFactory()
user = User.objects.get(username='jacob')
view = views.ProjectList.as_view()
# Make an authenticated request to the view...
request = factory.post(url, data=data,)
force_authenticate(request, user=user)
response = view(request)
self.assertEqual(response.status_code, 200)
When I am running test cases I am getting this error
(Only lists and tuples may be used in a list field: ['org_group'])
The complete Stack Trace is
ValidationError: Got a ValidationError when calling Project.objects.create().
This may be because request data satisfies serializer validations but not Mongoengine`s.
You may need to check consistency between Project and ProjectSerializer.
If that is not the case, please open a ticket regarding this issue on https://github.com/umutbozkurt/django-rest-framework-mongoengine/issues
Original exception was: ValidationError (Project:None) (Only lists and tuples may be used in a list field: ['org_group'])
Not getting why we cant pass object like this.
Same thing when I am posting as an request to same method It is working for me but test cases it is failing
The tests should be running using multipart/form-data, which means that they don't support lists or nested data.
You can override this with the format argument, which I'm guessing you probably want to set to json. Most likely your front-end is using JSON, or a parser which supports lists, which explains why you are not seeing this.

Is APITest with Query params different then just normal url?

I'm writing some unit tests against an API that either returns all the books, or only returns the books of the given genre in the query params. This seems to be working when I hit it in my local dev server. However, it doesn't even go into the else statement if the genre is specified in my unit test.
My unit test looks like this:
class TitlesAndBlurbsListTestCase(APITestCase):
def setUp(self):
# Creates a lot of books with genre horror
# and books not in the horror genre
def test_horror_genre(self):
# Ensure that screener can see all the available books
self.client.login(username='b', password='b')
response = self.client.get('/api/titles-and-blurbs/?genre=horror')
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Ensure that the screener gets all horror books at first
horror_books = TitlesAndBlurbs.objects.filter(genre='horror')
# I keep getting an assertion error here - it returns all the books
self.assertEqual(len(response.data), horror_books.count())
My api viewset looks like this
class TitlesAndBlurbsListViewSet(viewsets.mixins.ListModelMixin,
viewsets.mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
model = TitlesAndBlurbs
permission_classes = [ScreenerPermissions]
serializer_class = TitlesAndBlurbsSerializer
def get_queryset(self):
if self.action == 'list':
genre = self.request.QUERY_PARAMS.get('genre', None)
if not genre:
print 'i dont have query params of genre'
TitlesAndBlurbs.objects.all()
else:
print genre
TitlesAndBlurbs.objects.filter(genre=genre)
return TitlesAndBlurbs.objects.all()
my url/router looks like
router.register(r'api/titles-and-blurbs', TitlesAndBlurbsListViewSet)
When I hit the url 'api/titles-and-blurbs/?genre=horror' in my browser I get the print statement and titles and blurbs that have the genre horror. However, when I hit in the
test suite, I don't get the print statement genre I get the print statement of 'i dont have query params', and it returns all books. Any help is really
appreciated.
Try passing the query parameter as a data payload instead. Change the line in your test to:
response = self.client.get('/api/titles-and-blurbs/', {'genre': 'horror'})
Django docs here on the different ways to pass query parameters in urls.
Another person reported a similar issue with an empty QUERY_PARAMS while testing DRF (see here). It looks like they fixed it but maybe they missed something or you didn't get the update.
If someone comes across this, for me it helped to:to change the url from
/api/titles-and-blurbs?genre=horror
to
/api/titles-and-blurbs/?genre=horror
Both urls were working find in Postman, but only the second one is working properly in the tests.

HttpResponse object becomes string when passed to assertContains

I have a strange problem in a Django template test. When the test executes my view, the view returns an HttpResponse object. However, when I then pass that response object to the Django TestCase assertContains method, the response object becomes a string. Since this string doesn't have a 'status_code' attribute like a response object does, the test fails. Here's my code:
template_tests.py
from django.test import TestCase
from django.test.client import RequestFactory
class TestUploadMainPhotoTemplate(TestCase):
def setUp(self):
self.factory = RequestFactory()
def test_user_selects_non_jpeg_photo_file(self):
"""
User is trying to upload a photo file via a form
with an ImageField. However, the file doesn't have
a '.jpg' extension so the form's is_valid function, which
I've overridden, flags this as an error and returns False.
"""
with open('photo.png') as test_photo:
request = self.factory.post(reverse('upload-photo'),
{'upload_photo': '[Upload Photo]',
'photo': test_photo})
kwargs = {'template': 'upload_photo.html'}
response = upload_photo(request, **kwargs)
# pdb.set_trace()
self.assertContains(response, 'Error: photo file must be a JPEG file')
When I run this code in the debugger and do 'type(response)' before I call assertContains, I can see that 'response' is a HttpResponse object. However, when assertContains is called, I get this error:
AttributeError: 'str' object has no attribute 'status_code'
I set an additional breakpoint in the assertContains method at the location .../django/test/testcases.py:638:
self.assertEqual(response.status_code, status_code...
At this point, when I do 'type(response)' again, I see that it has become a string object and doesn't have a status_code attribute. Can anyone explain what's going on? I've used this same test pattern successfully in a dozen other template tests and it worked in all of them. Could it have something to do with the fact that this test involves uploading a file?
Thanks.
I had a similar problem and solved it by looking at assertContains, it doesn't really help you but who knows ?
void assertContains( SimpleTestCase self, WSGIRequest response, text, count = ..., int status_code = ..., string msg_prefix = ..., bool html = ... )
Asserts that a response indicates that some content was retrieved
successfully, (i.e., the HTTP status code was as expected), and that
text occurs count times in the content of the response.
If count is None, the count doesn't matter - the assertion is true
if the text occurs at least once in the response.
Could it have something to do with the fact that this test involves uploading a file?
Sure, as I successfully wrote my test for a simple HttpResponse :
response = self.client.get('/administration/', follow=True)
self.assertContains(response, '<link href="/static/css/bootstrap.min.css" rel="stylesheet">',msg_prefix="The page should use Bootstrap")
So I am not really helping, but maybe this could help somebody a little.
I had a similar problem handling Json Response .
self.assertEquals(json.loads(response.content),{'abc': True})
Following fixed the problem for me.