-
-
Notifications
You must be signed in to change notification settings - Fork 31.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed #29082 -- Made test Client appropriately encode JSON. #9645
Conversation
Fixed git. One commit now. Let me know if there is anything else worth changing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests/test_client/tests.py
Outdated
self.assertContains(response, 'This is a test') | ||
self.assertEqual(response.context['var'], '\xf2') | ||
self.assertEqual(response.templates[0].name, 'GET Template') | ||
|
||
def test_get_post_view(self): | ||
"GET a view that normally expects POSTs" | ||
"""GET a view that normally expects POSTs""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and the few following cleanups are strictly unrelated to this patch.
Presumably, they're here because of the feedback for similar items on #9636. There are lots of inconsistencies left in this file, around both docstrings and formatting of simple (e.g.) post_data
dicts.
Despite being strictly unrelated, I'm inclined to leave these few changes as they are here, and include them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep I'm starting to think I'll make a ticket to clean up this file.
Mind if I revert these back to how they were for that ticket instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NDevox I'd be very happy for you to do that. 🙂
(If it's just a single file cleanup, you can just make a PR, without the Trac ticket)
Hmmm. Now there's an |
@carltongibson I created separate PR #9649 to fix imports per |
Sorry about the delete yesterday 😞, long day for me that one and things weren't going well, probably should have left it for the morning. I squashed the commits, mainly to prove to myself I can use git. |
@NDevox No problem. It happens. 🙂 Looking at it, I think your issue probably arose from not fetching the latest updates and rebasing on that. (This morning I realised this branch too wasn't up to date and needed to rebase.) If you're careful about that it usually goes smoothly... OK. I merged in the |
@carltongibson removed the irrelevant docstring/comment changes and am happy to merge. |
OK. Thanks @NDevox! |
django/test/client.py
Outdated
@@ -309,6 +312,15 @@ def _encode_data(self, data, content_type): | |||
charset = settings.DEFAULT_CHARSET | |||
return force_bytes(data, encoding=charset) | |||
|
|||
def _encode_json(self, data, encoder): | |||
""" | |||
Return correctly encoded JSON if we have a dict. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid use of "we" in comments per our style guide.
tests/test_client/tests.py
Outdated
@@ -85,6 +87,69 @@ def test_post(self): | |||
self.assertEqual(response.templates[0].name, 'POST Template') | |||
self.assertContains(response, 'Data received') | |||
|
|||
def test_json_post(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try refactoring the tests to using a loop and subTest and it doesn't look nice to duplicate so much code..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned before I find doing this obfuscates what the tests are doing - especially when they are testing different things.
However if this is the standard in the lib I will do so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm to do this I'd have to pass in the test methods with some matching parameters which doesn't look elegant to me.
I've put the assertion code into a method and call that instead which means all these methods are now have 2 lines of code in them instead of repeating assertions.
tests/test_client/views.py
Outdated
A view which expects a request with the header 'application/json' and | ||
parseable json data to go with it. | ||
|
||
We expect a value named 'value' in the data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid use of "we" in comments per our style guide.
docs/topics/testing/tools.txt
Outdated
If you provide ``content_type`` as :mimetype:`application/json` the | ||
POST data will be presented as a binary string like so:: | ||
|
||
{"name": "fred", "passwd": "secret"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what this is demonstrating... this doesn't look like a binary string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was split between representing it as an actual binary string, or what the binary string represents. I'll switch it to look like a binary string.
django/test/client.py
Outdated
@@ -331,6 +343,8 @@ def get(self, path, data=None, secure=False, **extra): | |||
def post(self, path, data=None, content_type=MULTIPART_CONTENT, | |||
secure=False, **extra): | |||
"""Construct a POST request.""" | |||
if content_type == JSON_CONTENT: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be better to put the content type check in _encode_json for consistency with _encode_data and to avoid duplicating that in every put/patch/etc. method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pointed this out on the ticket, my only issue with it is I don't think it's clear that this check will happen with this method name.
django/test/client.py
Outdated
@@ -260,7 +262,8 @@ class RequestFactory: | |||
Once you have a request object you can pass it to any view function, | |||
just as if that view had been hooked up using a URLconf. | |||
""" | |||
def __init__(self, **defaults): | |||
def __init__(self, json_encoder=DjangoJSONEncoder, **defaults): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about using keyword-only args?
def __init__(self, *, json_encoder=DjangoJSONEncoder, **defaults):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I'm already doing all the doc changes and I'm pro this change so happy to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is better because it is more explicit and future proof if you want to add other params.
But wait for other comments :)
django/test/client.py
Outdated
@@ -418,8 +438,8 @@ class Client(RequestFactory): | |||
contexts and templates produced by a view, rather than the | |||
HTML rendered to the end-user. | |||
""" | |||
def __init__(self, enforce_csrf_checks=False, **defaults): | |||
super().__init__(**defaults) | |||
def __init__(self, enforce_csrf_checks=False, json_encoder=DjangoJSONEncoder, **defaults): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above use keyword-only args
def __init__(self, enforce_csrf_checks=False, *, json_encoder=DjangoJSONEncoder, **defaults):
0c5f204
to
c91c32c
Compare
tests/test_client/tests.py
Outdated
@@ -85,6 +87,52 @@ def test_post(self): | |||
self.assertEqual(response.templates[0].name, 'POST Template') | |||
self.assertContains(response, 'Data received') | |||
|
|||
def assert_json_response(self, response, method): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use CamelCase for assert methods to follow unittest
convention, e.g. assertJSONResponse
. Maybe it will be better to pass method
and data
instead of response
, e.g.:
def assertJSONResponse(method, data, method_name):
response = method('/json_view/', data, content_type=JSON_CONTENT)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['data'], 37)
self.assertEqual(response.templates[0].name, '{} Template'.format(method))
self.assertContains(response, 'Viewing {} page.'.format(method))
def test_json_post(self):
assertJSONResponse(self.client.post, {'value': 37}, 'POST')
def test_json_put(self):
assertJSONResponse(self.client.put, {'value': 37}, 'PUT')
...
Please also try to avoid temporary variables like post_data
, put_data
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
cbeb700
to
38d4e4a
Compare
@felixxm @carltongibson All ready now. Anything more? |
docs/releases/2.1.txt
Outdated
* Added test :class:`~django.test.Client` support for 307 and 308 redirects. | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this newline shouldn't be here :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't worry about that, I'm making edits to the patch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I amended the first commit with some of my changes by mistake but most of the revisions are in the second one. Let me know if anything looks off.
tests/test_client/tests.py
Outdated
"""DELETE some JSON data to a view""" | ||
self.assertJSONResponse(self.client.delete, {'value': 37}, 'DELETE') | ||
|
||
def test_encoded_json_post(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't include this test in the PR because it's testing existing behavior. But it doesn't seem needed as there's a test in test_client_regress
that fails if the isinstance(data, dict)
check is _encode_json
is removed.
@timgraham looks fine to me. Want me to squash or happy as is? |
https://code.djangoproject.com/ticket/29082
(continued from #9636)