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

Configurable expect method #394

Merged
merged 1 commit into from
Jan 20, 2025
Merged

Conversation

mhadam
Copy link
Contributor

@mhadam mhadam commented Jan 17, 2025

I wasn't able to match a request I was making and it turned out that I was serializing a set, and therefore wouldn't know the order of the JSON list that gets serialized. Due to the way JSON is compared in this library, order does matter (even if you pass a structure with a set to json=).

So I think it makes sense to allow passing in some sort of open interface or simply accept RequestMatcher and consumers could override methods.

This would allow composing RequestMatcher with deepdiff to do order insensitive matching or anything else you'd want to do.

@csernazs
Copy link
Owner

hi @mhadam ,

Thanks for the PR, it looks good.

I would also change these lines to call your new method:

request_handler = RequestHandler(matcher)
if handler_type == HandlerType.PERMANENT:
self.handlers.append(request_handler)
elif handler_type == HandlerType.ONESHOT:
self.oneshot_handlers.append(request_handler)
elif handler_type == HandlerType.ORDERED:
self.ordered_handlers.append(request_handler)
return request_handler

And also, a test would be needed for the new method.

Shall I do these for your PR or do you want to do that?

Zsolt

@mhadam
Copy link
Contributor Author

mhadam commented Jan 18, 2025

I can do this quick now, thanks for the review!

@csernazs
Copy link
Owner

Hi @mhadam ,

It just came to my mind that you could also use dirty-equals with pytest-httpserver, so it would match json lists having elements in arbitrary orders.

Example:

from pytest_httpserver import HTTPServer
import requests
from dirty_equals import IsList


def test_example(httpserver: HTTPServer):
    httpserver.expect_request(
        "/example", json={"list": IsList(1, 2, 3, 4, check_order=False)}, method="POST"
    ).respond_with_data("OK")

    resp = requests.post(httpserver.url_for("/example"), json={"list": [1, 2, 3, 4]})
    assert resp.text == "OK"

    resp = requests.post(httpserver.url_for("/example"), json={"list": [4, 3, 1, 2]})
    assert resp.text == "OK"

Here, if you create the IsList object it can be compared to a list and you can allow arbitrary order.

On the other hand, your PR seems completely legit and I'm happy to merge it (if you can make those small changes I mentioned), I just wanted to show you the capability of using dirty-equals with pytest-httpserver.

Thanks,
Zsolt

@mhadam
Copy link
Contributor Author

mhadam commented Jan 20, 2025

Hi Zsolt, thanks for this example it looks great. I didn't know about dirty-equals but I like it more than the method in this PR. I still think maybe it would be useful to have anyway just as an option.

In writing a test, I thought maybe it would make sense to compare what gets returned, that is, whether expect(...) and expect_request(...) called with the same arguments returns handlers doing the same thing. But a couple things would need to be done, implement RequestMatcher.__eq__ but also equality dunder on the other matcher classes which includes header and query matchers too. Maybe a lot to implement just for this PR, but does make sense to me.

But I think instead I'll implement it like your example above where you instantiate two equivalent expectations and see that they both match the same request.

tests/test_matcher.py Outdated Show resolved Hide resolved
tests/test_matcher.py Outdated Show resolved Hide resolved
@csernazs
Copy link
Owner

Hi Zsolt, thanks for this example it looks great. I didn't know about dirty-equals but I like it more than the method in this PR. I still think maybe it would be useful to have anyway just as an option.

Yes, personally I find dirty-equals to be an excellent library which is very useful when comparing elements (in pytest, especially) but now I realized it can make json matching on steroids as one json expectation (eg. a dict) could be matched to more request bodies (not just list order ignoring but regexp matching in strings, range matching for integers, etc).

In writing a test, I thought maybe it would make sense to compare what gets returned, that is, whether expect(...) and expect_request(...) called with the same arguments returns handlers doing the same thing. But a couple things would need to be done, implement RequestMatcher.__eq__ but also equality dunder on the other matcher classes which includes header and query matchers too. Maybe a lot to implement just for this PR, but does make sense to me.

No, the test what you wrote is ok, I think. I just had minor comments.

But I think instead I'll implement it like your example above where you instantiate two equivalent expectations and see that they both match the same request.

Yeah, I think the last part is not needed. But let me know if I missed the point.

Thanks again for your PR!

Zsolt

@csernazs
Copy link
Owner

hi @mhadam ,

Thanks for making the changes!

One last ask: could you please squash your commits to one commit with some commit msg?

I was not able to find an option on github UI to do that (and I want to show you as the author of the change).

Thanks,
Zsolt

@mhadam
Copy link
Contributor Author

mhadam commented Jan 20, 2025

sure thing and thanks so much for helping me put this together!

@csernazs csernazs merged commit d71bba2 into csernazs:master Jan 20, 2025
11 checks passed
@csernazs
Copy link
Owner

This has been merged :)

I'll probably release a new version tomorrow with your code.

Zsolt

@mhadam
Copy link
Contributor Author

mhadam commented Jan 20, 2025

Awesome, thanks again!

@mhadam mhadam deleted the feature/expect branch January 20, 2025 22:43
@mhadam
Copy link
Contributor Author

mhadam commented Jan 21, 2025

from deepdiff import DeepDiff
from dirty_equals import FunctionCheck
from typing import Callable, Any
import pytest_httpserver

def custom_compare(data) -> Callable[[Any], bool]:
    def inner(x):
        return len(DeepDiff(x, data, ignore_order=True)) == 0
    return inner

httpserver.expect_request(
    "/test", method="POST", json=FunctionCheck(custom_compare(req))
).respond_with_json("[1,2,3]")

Inspired by your suggestion above @csernazs, just wanted to share this snippet I got working with DeepDiff and dirty_equals, since dirty_equals alone doesn't give you generalized unordered comparisons at any level.

Hopefully this helps someone out there!

@csernazs
Copy link
Owner

hi @mhadam,

FYI, I've released version 1.1.1 which has your PR amongst other minor changes.
You can find it on pypi: https://pypi.org/project/pytest_httpserver/

Zsolt

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.

2 participants