Skip to content
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

Merged
merged 1 commit into from
Feb 6, 2018

Conversation

NDevox
Copy link
Contributor

@NDevox NDevox commented Jan 31, 2018

@NDevox
Copy link
Contributor Author

NDevox commented Jan 31, 2018

@felixxm @carltongibson

Fixed git. One commit now. Let me know if there is anything else worth changing.

Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. This looks right.

@NDevox: In general, we could have saved the original PR (but not once you deleted the repo... 🙂)

I included a few changes from the previous feedback from @felixxm. I will squash the commits when merging.

Thanks for the effort.

// @felixxm: If you're happy I'll merge.

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"""
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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)

@carltongibson
Copy link
Member

Hmmm. Now there's an isort failure (which looks totally unrelated...). Coffee first.

@felixxm
Copy link
Member

felixxm commented Feb 1, 2018

@carltongibson I created separate PR #9649 to fix imports per isort 4.3.0.

@NDevox
Copy link
Contributor Author

NDevox commented Feb 1, 2018

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.

@carltongibson
Copy link
Member

@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 isort fix. This should be good to go...

@carltongibson carltongibson changed the title Fixes #29082 -- Encodes JSON appropriately in the test Client. Fixes #29082 -- Made test Client appropriately encode JSON Feb 1, 2018
@felixxm felixxm changed the title Fixes #29082 -- Made test Client appropriately encode JSON Fixed #29082 -- Made test Client appropriately encode JSON. Feb 1, 2018
@NDevox
Copy link
Contributor Author

NDevox commented Feb 1, 2018

@carltongibson removed the irrelevant docstring/comment changes and am happy to merge.

@carltongibson
Copy link
Member

OK. Thanks @NDevox!

@@ -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.
Copy link
Member

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.

@@ -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):
Copy link
Member

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..

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

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.
Copy link
Member

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.

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"}
Copy link
Member

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.

Copy link
Contributor Author

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.

@@ -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:
Copy link
Member

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.

Copy link
Contributor Author

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.

@@ -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):
Copy link
Contributor

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):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth it?

Copy link
Contributor Author

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.

Copy link
Contributor

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 :)

@@ -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):
Copy link
Contributor

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):

@NDevox NDevox force-pushed the master branch 2 times, most recently from 0c5f204 to c91c32c Compare February 1, 2018 17:39
@@ -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):
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@NDevox NDevox force-pushed the master branch 2 times, most recently from cbeb700 to 38d4e4a Compare February 5, 2018 10:22
@NDevox
Copy link
Contributor Author

NDevox commented Feb 5, 2018

@felixxm @carltongibson All ready now.

Anything more?

* Added test :class:`~django.test.Client` support for 307 and 308 redirects.


Copy link
Sponsor Member

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 :)

Copy link
Member

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.

@django django deleted a comment from NDevox Feb 6, 2018
@django django deleted a comment from NDevox Feb 6, 2018
@django django deleted a comment from NDevox Feb 6, 2018
@django django deleted a comment from NDevox Feb 6, 2018
@django django deleted a comment from NDevox Feb 6, 2018
@django django deleted a comment from NDevox Feb 6, 2018
Copy link
Member

@timgraham timgraham left a 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.

"""DELETE some JSON data to a view"""
self.assertJSONResponse(self.client.delete, {'value': 37}, 'DELETE')

def test_encoded_json_post(self):
Copy link
Member

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.

@NDevox
Copy link
Contributor Author

NDevox commented Feb 6, 2018

@timgraham looks fine to me.

Want me to squash or happy as is?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants