mindflayer / python-mocket

a socket mock framework - for all kinds of socket animals, web-clients included
BSD 3-Clause "New" or "Revised" License
279 stars 41 forks source link

Question: function responses and regex #200

Closed janrito closed 1 year ago

janrito commented 1 year ago

We are using httpretty's response body from functions and regexed url matching in order to test timeouts and redirects. I'm not suggesting this is the right way, but haven't figured out how to replicate these using mocket.

import re
from collections.abc import Generator
from http import HTTPStatus
from itertools import count
from threading import Event
from urllib.parse import urljoin

import httpretty
import pytest
import requests
from httpretty.core import HTTPrettyRequest

MAX_REDIRECTS = 5
TIMEOUT = 1

@pytest.fixture
def httpretty_mock() -> Generator[None, None, None]:
    httpretty.enable()
    yield
    httpretty.reset()
    httpretty.disable()

def test_delay(httpretty_mock: None) -> None:
    test_url = "http://github.com"
    event = Event()

    def delayed(
        request: HTTPrettyRequest, uri: str, response_headers: dict[str, str]
    ) -> tuple[int, dict[str, str], bytes]:
        event.wait(TIMEOUT + 1)
        return (HTTPStatus.OK, {}, b"")

    httpretty.register_uri(httpretty.GET, test_url, body=delayed)

    with pytest.raises(requests.exceptions.Timeout):
        requests.get(test_url, timeout=TIMEOUT)

def test_redirect(httpretty_mock: None) -> None:
    test_url = "http://github.com"
    test_url_matcher = re.compile(r"http:\/\/github\.com(?:\/\w+)?")
    num = count()

    def redirected(
        request: HTTPrettyRequest, uri: str, response_headers: dict[str, str]
    ) -> tuple[int, dict[str, str], bytes]:
        n_request = next(num)
        if n_request < MAX_REDIRECTS:
            return (
                HTTPStatus.MOVED_PERMANENTLY,
                {"Location": urljoin(uri, str(n_request))},
                b"",
            )
        else:
            return (HTTPStatus.OK, {}, b"")

    httpretty.register_uri(httpretty.GET, test_url_matcher, body=redirected)

    res = requests.get(test_url)
    assert res.status_code == HTTPStatus.OK
    assert res.url == urljoin(test_url, str(MAX_REDIRECTS - 1))
mindflayer commented 1 year ago

I've never introduced functions and regexes because I consider both bad practices. I don't like the idea of having dynamic tests, and I'd rather use something like pytest parametrize, but I am open to discuss it further.

janrito commented 1 year ago

Yes, that sentence is the reason I've asked the question. I don't know of another way of achieving what I need without functions. I was hoping that there was a different method, that I just hadn't found it yet.

mindflayer commented 1 year ago

You probably need something like:

from mocket.mockhttp import Entry, Response

responses = [Response(status=302, match_querystring=False)] * MAX_REDIRECTS + [Response(match_querystring=False)]

Entry.register(Entry.GET, test_url, *responses)
mindflayer commented 1 year ago

While for the test using delayed you could have Mocket raising an exception for you instead. See here.

janrito commented 1 year ago

You probably need something like:

from mocket.mockhttp import Entry, Response

responses = [Response(status=302, match_querystring=False)] * MAX_REDIRECTS + [Response(match_querystring=False)]

Entry.register(Entry.GET, test_url, *responses)

Oh this is great. I hadn't thought of multiple Response objects.

While for the test using delayed you could have Mocket raising an exception for you instead. See here.

I see. I’m not sure why this feels less like what we wanted to test, but it might be good enough. I will give it a try