-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Fix #4829: Reduce code redundancy in backend testing #5838
Conversation
Codecov Report
@@ Coverage Diff @@
## develop #5838 +/- ##
========================================
Coverage 45.21% 45.21%
========================================
Files 523 523
Lines 30722 30722
Branches 4605 4605
========================================
Hits 13892 13892
Misses 16830 16830 Continue to review full report at Codecov.
|
Hi @lilithxxx, I'm going to mark this PR as stale because it hasn't had any updates for 10 days. If no further activity occurs within 7 days, it will be automatically closed so that others can take up the issue. |
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 looks great in general, thanks @lilithxxx! Left some inline comments.
Also, does this remove all occurrences of self.testapp.get in the codebase?
core/controllers/admin_test.py
Outdated
@@ -38,26 +38,28 @@ def setUp(self): | |||
def test_admin_page_rights(self): | |||
"""Test access rights to the admin page.""" | |||
|
|||
response = self.testapp.get('/admin') | |||
response = self.get_html('/admin', expect_errors=True, |
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.
Here and elsewhere, status codes beginning with 2 or 3 should not be considered errors.
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.
If that's the case then should'nt the condition here change to assert both 2xx and 3xx codes?
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.
Yes, I think so. Thanks for catching that!
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
core/controllers/admin_test.py
Outdated
@@ -38,26 +38,28 @@ def setUp(self): | |||
def test_admin_page_rights(self): | |||
"""Test access rights to the admin page.""" | |||
|
|||
response = self.testapp.get('/admin') | |||
response = self.get_html('/admin', expect_errors=True, |
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.
Here and below, prefer breaking after '(' if contents of parens span multiple lines.
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
# https://github.com/Pylons/webtest/blob/ | ||
# bf77326420b628c9ea5431432c7e171f88c5d874/webtest/app.py#L1119 . | ||
self.assertEqual(html_response.status_int, expected_status_int) | ||
if not expect_errors: |
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.
expect_errors is only for status checks starting with 4 or 5.
Note that you should also check the converse, i.e. that if expect_errors is True then the status_int starts with 4 or 5.
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
@seanlip this does not remove all occurrences of
There are a few other functions with similar problems(some has attribute audio etc) |
Thanks @lilithxxx. I don't understand the first one, I don't see 'status_code' in the response? (And in any case I don't understand why that behaves differently from similar json responses.) For the content-type attrs maybe add an extra arg in the test_utils function to overwrite the default check? I think if we are going to get rid of self.testapp.get() we should do it properly (i.e. completely), so please don't hesitate to ask me if there are any other cases which you'd like me to suggest a solution for. Thanks! |
Hi @seanlip. There's another conflict in base.py -> Here the error codes can be 2xx, 3xx, or 4xx so the assertion fails in
There is one more error like this in collection_editor_test. What should I do about them? |
For the second one, since you're making individual requests, can't you pass the expected status code in with each request? For the first one, you have a couple of options -- either generalize the method signature of get_json to accept a list of allowed expected status codes, or create a new method in test_utils that is similar to get_json but accepts a list of allowed codes instead of just a single one. The second might be better in order to keep the default get_json() simple. |
Hi @lilithxxx. Due to recent changes in the "develop" branch, this PR now has a merge conflict. Please follow this link if you need help resolving the conflict, so that the PR can be merged. Thanks! |
@seanlip The |
I looked at the code for get_json(). It is doing a status verification check: https://github.com/oppia/oppia/blob/develop/core/tests/test_utils.py#L512 So, not sure what you mean by "we can't check status codes with this function". I'm talking about modifying the logic of get_json itself, or writing a function that is similar to get_json but that accepts a list of expected status codes instead of just one. I feel like I might be missing something in your response, though, since the original discussion wasn't about generalizing to html/image/audio/etc. |
I think I was not very clear, pardon me. Yes it does a status verification check but that is on the response object which we get from |
Thanks for explaining. I think the problem you're encountering is one I addressed in my previous comment, though? I.e. you wouldn't change get_json to return the status code. You'd pass the expected status code(s) into get_json or similar and get_json (or the other function) would check those codes internally. |
@seanlip I renamed the function |
core/tests/test_utils.py
Outdated
html_response.content_type, 'text/html') | ||
|
||
return html_response | ||
if not can_have_multiple_responses: |
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 can_have_multiple_responses is a bit of a misleading variable name. I think what you're trying to convey is more like "can be an error or not", i.e. something that contradicts expect_errors being a boolean.
So I think maybe it's worth leaving this method signature as is (I am fine with changing get_html to get_response) and creating a separate method along the lines of get_response_without_checking_for_errors(). Then have the base.py test that runs through all the URLs use that latter method instead. In fact, maybe this method here could use that method too.
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
305d706
to
014d687
Compare
@seanlip made changes. PTAL |
Hi @lilithxxx. Due to recent changes in the "develop" branch, this PR now has a merge conflict. Please follow this link if you need help resolving the conflict, so that the PR can be merged. Thanks! |
Hi @lilithxxx, any updates on this? Thanks! |
I will get back to it tomorrow as I am travelling currently. Thanks |
Hi @lilithxxx. Due to recent changes in the "develop" branch, this PR now has a merge conflict. Please follow this link if you need help resolving the conflict, so that the PR can be merged. Thanks! |
Hi @lilithxxx, I'm going to mark this PR as stale because it hasn't had any updates for 10 days. If no further activity occurs within 7 days, it will be automatically closed so that others can take up the issue. |
Hi @seanlip PTAL at this as this PR has been marked stale.Thanks! |
Sorry @lilithxxx for the delay. Could you please resolve the merge conflicts first? |
@seanlip done! |
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.
Thanks @lilithxxx -- did another pass, PTAL.
core/tests/test_utils.py
Outdated
|
||
Args: | ||
url: str. The URL to fetch the response. | ||
|
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.
Drop line breaks between successive arg descriptions (please follow existing codebase style). In addition, for line descriptions that span more than one line, indent the continuation line by 4 spaces (again, follow the existing style).
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
core/tests/test_utils.py
Outdated
""" | ||
have_errors = False | ||
if expected_status_int >= 400 and ( | ||
expected_status_int < 600): |
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 think you need this second part -- just expected_status_int >= 400 should suffice.
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
core/tests/test_utils.py
Outdated
expected_status_int=200, | ||
expected_content_type='text/html'): | ||
"""Get a response, transformed to a Python object.""" | ||
def get_response( |
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.
Make private (by prepending underscore)? That ensures it doesn't get used elsewhere -- only by get_html_response 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.
get_response
is being used by other functions where the content-type is different than html or json -- for example png or text/plain.
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.
Ah, I see. Then, I suggest: make get_response private, and define a new get_custom_response that first checks that the content type is not html/json, and that then calls _get_response(). External callers should use get_custom_response(). I think that would be a cleaner API.
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
core/tests/test_utils.py
Outdated
Args: | ||
url: str. The URL to fetch the response. | ||
|
||
params: str or dict. A query string, or a dictionary that will be |
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.
See above notes on how to write docstrings and on the idea of making the expected usage of params more explicit.
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
core/tests/test_utils.py
Outdated
expected_status_int_list: list(int). A list of integer status | ||
code to expect. | ||
|
||
params: str or dict. A query string, or a dictionary that will be |
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.
Ditto.
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
core/tests/test_utils.py
Outdated
"""Get a JSON response, transformed to a Python object.""" | ||
have_errors = False |
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.
Var name should probably be expect_errors. Currently you are trying to have this variable state whether the test expects an error after the response comes back. It doesn't say whether the response actually has an error or not.
Similar comments might apply elsewhere; please check.
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
Hi @seanlip I made some changes, can you PTAL? |
core/controllers/reader_test.py
Outdated
csrf_token = self.get_csrf_token_from_response(response) | ||
summaries = self.get_json( | ||
recommendations_url, params=csrf_token)['summaries'] | ||
self.get_html_response('/explore/%s' % exploration_id) |
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.
Why do you need this call?
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.
For checking status code and content-type?
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.
Hm. The original purpose of the call was to get the response so you can get the CSRF token so that you can make the subsequent call.
If you don't need the CSRF token then do you actually need this?
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.
Hmm I think you are right we don't need this now.
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
core/tests/test_utils.py
Outdated
if expected_status_int >= 400 and ( | ||
expected_status_int < 600): | ||
have_errors = True | ||
if params: |
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.
if params is not None
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
core/controllers/editor_test.py
Outdated
response = self.get_response(download_url, | ||
expected_content_type='text/plain') | ||
response = self.get_custom_response( | ||
download_url, expected_content_type='text/plain') |
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.
You don't need to specify the argname for expected_content_type, since it has no default value.
The linter should catch this. Maybe file a bug?
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
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.
Please address my last paragraph, either with a link to the issue filed or some explanation for why the linter didn't catch this.
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.
So sorry -- I was looking into this and I think pylint reports when we don't use the argname in case of a default value parameter but not the other way around. @apb7 Can you confirm this? So I think this is to fall under the category of a new feature. I am opening an issue
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.
Opened #6067
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.
@seanlip @lilithxxx, I can confirm this. Thanks for filling the issue, @lilithxxx.
core/controllers/resources_test.py
Outdated
expected_content_type='image/png') | ||
response = self.get_custom_response( | ||
self._get_image_url('0', filename), | ||
expected_content_type='image/png') |
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.
Ditto.
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
@seanlip PTAL |
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.
LGTM. Thanks @lilithxxx!
Explanation
Fixes #4829
A new function is introduced
get_html
to get a HTML response analogous toget_json
Replaces instances of
self.testapp.get
with eitherself.get_json
orself.get_html
to reduce code redundancy.Checklist
python scripts/pre_commit_linter.py
andbash scripts/run_frontend_tests.sh
.