-
-
Notifications
You must be signed in to change notification settings - Fork 355
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
Calls info in BaseResponse #664
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
📢 Thoughts on this report? Let us know!. |
responses/__init__.py
Outdated
self._calls.add(request, response) | ||
match.calls.add_call(self._calls[next_index]) |
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 you have the request and response, why do you need to read out of self._calls
? Wouldn't match.calls.add()
work?
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.
Actually, that's what I did initially. But then an idea came up:
- reuse an already created call from the "global" list
self._calls
- have the same call instance in the global list and in the individual response list (
match.calls
)
(using match.calls.add()
we'll create a separate instance of the same 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.
Ok, wanting to reuse the objects seems reasonable to me. What do you think of something like
call = Call(request, response)
self._calls.add_call(call)
match.calls.add_call(call)
That would save us having to index/count call objects.
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.
Looks good, thanks, Let me try...
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 replaced the index with
call = Call(request, response)
self._calls.add_call(call)
match.calls.add_call(call)
and added an example of usage Response.calls into the README.
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.
also, CHANGELOG entry is missing
README.rst
Outdated
assert len(responses.calls) == 3 # total calls count | ||
# Assert calls to the 1st resource ("http://www.example.com"): | ||
assert rsp.call_count == 2 | ||
assert rsp.calls[0] is responses.calls[0] | ||
assert rsp.calls[1] is responses.calls[1] | ||
request = rsp.calls[0].request | ||
assert request.url == "http://www.example.com/" | ||
assert request.method == "GET" | ||
request = rsp.calls[1].request | ||
assert request.url == "http://www.example.com/?hello=world" | ||
assert request.method == "GET" | ||
# Assert calls to the 2nd resource (http://foo.bar): | ||
assert rsp2.call_count == 1 | ||
assert rsp2.calls[0] is responses.calls[2] | ||
request = rsp2.calls[0].request | ||
assert request.url == "http://foo.bar/42/" | ||
assert request.method == "PUT" | ||
request_payload = json.loads(request.body) | ||
assert request_payload == {"name": "Bazz"} |
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 could be sparse to increase readability
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.
The example has been replaced. Is this still relevant?
Co-authored-by: Maksim Beliaev <beliaev.m.s@gmail.com>
Added the CHANGELOG entry. |
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
This is looking good, a few small formatting issues in the README that need to be fixed up before this can be merged. |
Fixed. |
It looks like mypy problems is related to v1.6.1 that released several days ago. I added several ignore comments related to my changes and old code. Please review again. |
responses/matchers.py
Outdated
@@ -13,7 +13,7 @@ | |||
from urllib.parse import urlparse | |||
|
|||
from requests import PreparedRequest | |||
from requests.packages.urllib3.util.url import parse_url | |||
from requests.packages.urllib3.util.url import parse_url # type: ignore[import-untyped] |
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 is that ?
responses/__init__.py
Outdated
@@ -252,11 +258,11 @@ def __len__(self) -> int: | |||
return len(self._calls) | |||
|
|||
@overload | |||
def __getitem__(self, idx: int) -> Call: | |||
def __getitem__(self, idx: int) -> Call: # type: ignore[misc] |
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.
too many ignores, something looks to be incorrect
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.
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.
Merged, thanks!
# Conflicts: # responses/__init__.py # responses/matchers.py
responses/tests/test_responses.py
Outdated
assert rsp.call_count == 2 | ||
request = rsp.calls[0].request | ||
assert request.url == "http://www.example.com/" | ||
assert request.method == "GET" | ||
request = rsp.calls[1].request |
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 we can make it a bit more readable by enumerating request
objects as well
assert rsp.call_count == 2 | |
request = rsp.calls[0].request | |
assert request.url == "http://www.example.com/" | |
assert request.method == "GET" | |
request = rsp.calls[1].request | |
assert rsp.call_count == 2 | |
request1 = rsp.calls[0].request | |
assert request.url == "http://www.example.com/" | |
assert request.method == "GET" | |
request2 = rsp.calls[1].request |
and so on
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 sense. Made tests readable. Please take a look
responses/tests/test_responses.py
Outdated
request = rsp2.calls[0].request | ||
assert request.url == "http://www.foo.bar/42/" | ||
assert request.method == "PUT" | ||
request_payload = json.loads(request.body) |
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 exactly do we test here ?
why do we need to test that request body could be converted?
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 changed the test a little)
Here we validate that request rsp2_request1 = rsp2.calls[0].request
obtained from the mock's call contains expected payload request_payload = {"name": "Bazz"}
, that was used on the put request.
request body could be converted?
PreparedRequest
contains only encoded data in the body
field, so we decode it to compare with the original data - request_payload
.
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.
PreparedRequest
contains only encoded data in thebody
field, so we decode it to compare with the original data -request_payload
.
correct, but do we need to check it at all as part of responses
?
we attach request
object as is without modification
responses/responses/__init__.py
Line 1041 in 5c6a3b1
response.request = request |
and in the case of this PR we can skip this check since it does not add a value.
although, you can fire a parallel PR that can add a separate test to validate that we have all the required data is attached to a request
we still will need to discuss if that is required or not but this we can do in a separate thread
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 attach request object as is without modification
You are right, I missed this moment.
So, in other words, in the current PR it is enough to test that calls of local mocks are identical to calls of the global list without deep validation of rsp.calls[N].request
& rsp.calls[N].response
?
I.e. merge all new tests (test_response_and_requests_mock_calls_are_equal
, test_response_call_request
, test_response_call_response
) to something like this:
def test_response_calls_and_registry_calls_are_equal():
@responses.activate
def run():
rsp1 = responses.add(responses.GET, "http://www.example.com")
rsp2 = responses.add(responses.GET, "http://www.example.com/1")
requests.get("http://www.example.com")
requests.get("http://www.example.com/1")
requests.get("http://www.example.com")
assert len(responses.calls) == len(rsp1.calls) + len(rsp2.calls)
assert rsp1.call_count == 2
assert len(rsp1.calls) == 2
assert rsp1.calls[0] is responses.calls[0]
assert rsp1.calls[1] is responses.calls[2]
assert rsp2.call_count == 1
assert len(rsp2.calls) == 1
assert rsp2.calls[0] is responses.calls[1]
run()
assert_reset()
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.
@beliaev-maksim and what about this question?
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.
@zeezdev sorry, missed this one in a long thread. Yes.
And if for some reason we have missed the tests that ensure the quality of Call
objects. Then I would recommend to create separate tests for those and open it in a separate PR.
responses/tests/test_responses.py
Outdated
assert response.content == b"test" | ||
assert response.status_code == 200 | ||
assert rsp2.call_count == 1 | ||
response = rsp2.calls[0].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.
same here, could be enumerated. Something like
response = rsp2.calls[0].response | |
response2 = rsp2.calls[0].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.
agree, 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.
I think we are very close to the conclusion
technically all looks good, just need to simplify user facing docs
|
||
|
||
@responses.activate | ||
def test_assert_calls_on_resp(): |
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 understand that this example might look very close to what you have in production. But can we unload it a bit and reduce complexity?
we probably still want to show an example of concurrent
since this could be one of the main usages, but we do not need all those complicated matrices of settings to show the usage
can you please simplify it to remove additional arguments and have more clear names like
rsp1, rsp2, rsp3
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.
Got you, I'll try to simplify 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.
simplified, please take a look
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.
@zeezdev now looks a way better, thank you!
README.rst
Outdated
for future in concurrent.futures.as_completed(future_to_uid): | ||
uid = future_to_uid[future] | ||
response = future.result() | ||
print("%s updated with %d status code" % (uid, response.status_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.
Can you please update it with f-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.
done
|
||
|
||
@responses.activate | ||
def test_assert_calls_on_resp(): |
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.
@zeezdev now looks a way better, thank you!
@markstory can you please do a final pass here |
rsp2 = responses.add(responses.GET, "http://www.example.com/1") | ||
rsp3 = responses.add( | ||
responses.GET, "http://www.example.com/2" | ||
) # won't be requested |
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.
that comment is probably true only with the default registry, or?
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 mean, this mocked resource (http://www.example.com/2
) isn't requested during the test to explain why we get empty rsp3.calls
.
I can remove this if it confuses.
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.
that is fine to keep it for the testing purpose.
I just mean that if the user applies a different registry or we switch the default registry (which is unlikely), then this comment will not be correct
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.
but this comment relates to the specific scope of the test. And is it relevant in this case?
If I were to initialize the test with
@responses.activate(registry=OrderedRegistry)
then it would be a different case with different expectations, wouldn’t 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.
that is more of a note rather than a call for an action :)
I am +1 with the current state of PR
now wait for @markstory to make his round of review
@zeezdev great work, thanks! |
Sometimes it is more convenient to get information about the calls made directly from the mockend request, instead of calculating the order of your calls in the global list (
responses.calls[n]
).What do you think of such a possibility?