Search code examples
pythonpytestrequests-mock

Python Mock_requests: Can I use wildcards in the url parameter of the Mocker? How to achieve pattern matching of urls when used with pytest?


I have written a test that tries to mock an exception as a side effect of a requests get operation. I am using the requests_mock library as shown in the code listing below:

def test_owm_client_raises_error_for_timeout_error(owm_proxy: OWMProxy) -> None:
  
      with requests_mock.Mocker() as m:
          m.get(
              "http://api.openweathermap.org/geo/1.0/direct/*",
              exc=request_exceptions.Timeout,
          )
  
          city = "london"
          utc_now = datetime.now(timezone.utc)
          target_time = int((utc_now + timedelta(hours=10)).timestamp())
  
          with pytest.raises(APIException):
              owm_proxy.for_time(city, datetime.fromtimestamp(target_time).isoformat())

Is it possible to mock a url using wildcard parameters, e.g. http://api.openweathermap.org/geo/1.0/direct/*? So far, the only way that I can simulate the desired effect of mocking a Timeout error is using requests.ANY as the url parameter in the mock.


Solution

  • Create a regex pattern via re.compile() which you can input to the requests mocker as documented:

    import re
    
    import pytest
    import requests
    import requests_mock
    
    
    def test_owm_client_raises_error_for_timeout_error():
          with requests_mock.Mocker() as m:
              # This is the regex pattern. Currently, it will accept any text after the base URL below. Update this if your requirements are stricter.
              matcher = re.compile('http://api.openweathermap.org/geo/1.0/direct/.*')
    
              # For simplicity, let's say we will just raise ValueError for the target URL
              m.get(
                  matcher,
                  exc=ValueError("Mocked error!"),
              )
    
              # Test if the exception ValueError will be raised if we accessed the target URLs
              with pytest.raises(ValueError):
                requests.get("http://api.openweathermap.org/geo/1.0/direct/")
              with pytest.raises(ValueError):
                requests.get("http://api.openweathermap.org/geo/1.0/direct/abcde")
              with pytest.raises(ValueError):
                requests.get("http://api.openweathermap.org/geo/1.0/direct/12345")
              with pytest.raises(ValueError):
                requests.get("http://api.openweathermap.org/geo/1.0/direct/abcde/12345")
              with pytest.raises(ValueError):
                requests.get("http://api.openweathermap.org/geo/1.0/direct/abcde/12345?any=thing&in=here")
    
              # Test if the mocked request wouldn't be used if the URL is different. Thus, the exception should show that the URL wasn't mocked.
              with pytest.raises(requests_mock.exceptions.NoMockAddress):
                requests.get("http://api.openweathermap.org/geo/1.0/direct2/")
              with pytest.raises(requests_mock.exceptions.NoMockAddress):
                requests.get("http://api.openweathermap.org/geo/1.0/direct2/abcde")
    

    Output:

    $ pytest -q
    .
    1 passed in 0.07s