Lior Dahan
Lior Dahan

Reputation: 762

requests_mock NoMockAddress Exception

I'm currently trying to do unit tests using the requests_mock library in order to patch requests. I've written the following code:

def test_general_search(requests_mock):
    params = {'query': 'testsetset', 'exact': 0, 'max_pages': '1', 'category': 'username'}
    requests_mock.get(f'{MOCK_ADDRESS}', json=MOCK_RESPONSE)
    client = Client(MOCK_ADDRESS, auth=MOCK_CREDS, headers=MOCK_HEADERS)
    res = general_search(client, params)
    assert ...

But I am getting the following error:

requests_mock.exceptions.NoMockAddress: No mock address: GET https://fake-url.com/search?query=username%3Atestsetset&page=1

Can anyone help me out with figuring out how to solve it? I've used mock as a pytest feature.

Upvotes: 20

Views: 17383

Answers (3)

Saikat Karmakar
Saikat Karmakar

Reputation: 230

I have faced this issue recenlt. I'm not sure of the reason. Maybe this can help someone.

  • Error

MOCK_SERVER_URL = "http://localhost:12345/"

def test_http_with_requests_mock(requests_mock):
    HTML_RESPONSE = "<html><body><h1>Some error!</h1></body></html>"
    requests_mock.get(MOCK_SERVER_URL + "/endpoint", text=HTML_RESPONSE)
[...]
  • Fix. Replaced the value of the variable to a single string
def test_http_with_requests_mock(requests_mock):
    HTML_RESPONSE = "<html><body><h1>Some error!</h1></body></html>"
    requests_mock.get("http://localhost:12345/endpoint", text=HTML_RESPONSE)
[...]

Upvotes: 1

therightstuff
therightstuff

Reputation: 1027

I would recommend using a custom matcher that performs a match on the request's URL not including the querystring.

The documentation isn't very clear on how to go about this, but after breaking my head for a while I was able to put together this article explaining how custom matchers for requests_mock work.

A custom matcher simply returns whether the request matched or not, for your use case you can even configure a single matcher to check for different URLs, or partial URLs, based on the URL in the request being matched.

The sample I created will hopefully make it clear enough for your use case:

from http import HTTPStatus
from urllib.parse import urlparse

import requests
import requests_mock


def a_matcher(request):
    # will match the base url with any query string
    parts = urlparse(request.url)
    base_url = parts.scheme + "://" + parts.netloc + parts.path
    if base_url == "http://www.example.com/path/to/the/magic":
        return True
    return None


def b_matcher(request):
    # will only match exactly
    if request.url == "http://www.example.com/road/to/hell":
        return True

    return None


def test_calls_made_out_of_order():
    status_code_a = int(HTTPStatus.OK)
    status_code_b = int(HTTPStatus.UNPROCESSABLE_ENTITY)

    with requests_mock.Mocker() as mocker:
        mocker.post(
            requests_mock.ANY,
            additional_matcher=a_matcher,
            status_code=status_code_a,
        )

        mocker.post(
            requests_mock.ANY,
            additional_matcher=b_matcher,
            status_code=status_code_b,
        )

        response_b = requests.post(
            "http://www.example.com/road/to/hell",
            headers={
                "Accept": "application/json",
            },
            json={
                "data": {
                    "b": True,
                }
            },
        )

        assert response_b.status_code == status_code_b

        response_a = requests.post(
            "http://www.example.com/path/to/the/magic?foo=bar",
            headers={
                "Accept": "application/json",
            },
            json={
                "data": {
                    "a": True,
                }
            },
        )

        assert response_a.status_code == status_code_a

Upvotes: 1

Michael Millar
Michael Millar

Reputation: 1604

This occurs when something about the mock request

in this case

requests_mock.get(f'{MOCK_ADDRESS}', json=MOCK_RESPONSE)

does not match exactly the get request of the code being tested. So I'd say you need to print out both the URL in the test class and the one in the tested code.

For example, I was getting the same error, until I did something like

    @requests_mock.Mocker(kw='mock')
    def test_get_data_from_endpoints(self, **kwargs):
        kwargs['mock'].get('http://testurl.org', json=response_1)

More info here: https://requests-mock.readthedocs.io/en/latest/matching.html#query-strings

Upvotes: 12

Related Questions