1

I have a method which is calling two different end points and validating there response.

def foo_bar:
  status_1 = requests.post(
        "http://myapi/test/status1", {},
        headers=headers)

  status_2 = requests.post(
        "http://myapi/test/status2", {},
        headers=headers)
  
 # and check the responses ...

I want to mock the both the url in pytest like this:

def foo_test:
   with requests_mock.Mocker() as m1:
       m1.post('http://myapi/test/status1',
               json={},
               headers={'x-api-key': my_api_key})
       
       m1.post('http://myapi/test/status2',
               json={},
               headers={'x-api-key': my_api_key})

It always throws the error

**NO mock address: http://myapi/test/status2**

seems like its only mocking first url.

So is there any way to mock more than one url in one method?

Sai prateek
  • 11,842
  • 9
  • 51
  • 66

3 Answers3

1

I think you have something else going on, it's very normal to mock out a single path like this at a time so that you can return different values from different paths simply. Your example works for me:

import requests
import requests_mock

with requests_mock.Mocker() as m1:
    my_api_key = 'key'

    m1.post('http://myapi/test/status1',
            json={},
            headers={'x-api-key': my_api_key})

    m1.post('http://myapi/test/status2',
            json={},
            headers={'x-api-key': my_api_key})

    headers = {'a': 'b'}

    status_1 = requests.post("http://myapi/test/status1", {}, headers=headers)
    status_2 = requests.post("http://myapi/test/status2", {}, headers=headers)

    assert status_1.status_code == 200
    assert status_2.status_code == 200
jamielennox
  • 368
  • 1
  • 2
  • 9
0

Yes there is. From the docs: "There is a special symbol at requests_mock.ANY which acts as the wildcard to match anything. It can be used as a replace for the method and/or the URL."

import requests_mock

with requests_mock.Mocker() as rm:
    rm.post(requests_mock.ANY, text='resp')

I am not sure if this is the best way but it works for me. You can assert afterwards which URLs were called with:

urls = [r._request.url, for r in rm._adapter.request_history]
0

Yes, there is a way!

You need to use additional_matcher callback (see docs) and requests_mock.ANY as URL.

Your example (with context manager)

import requests
import requests_mock


headers = {'key': 'val', 'another': 'header'}


def my_matcher(request):
    url = request.url
    mocked_urls = [
        "http://myapi/test/status1",
        "http://myapi/test/status2",
    ]
    return url in mocked_urls  # True or False

# as Context manager
with requests_mock.Mocker() as m1:
    m1.post(
        requests_mock.ANY,  # Mock any URL before matching
        additional_matcher=my_matcher,  # Mock only matched
        json={},
        headers=headers,
    )
    r = requests.post('http://myapi/test/status1')
    print(f"{r.text} | {r.headers}")
    r = requests.post('http://myapi/test/status2')
    print(f"{r.text} | {r.headers}")

    # r = requests.get('http://myapi/test/status3').text  # 'NoMockAddress' exception

Adaptation for pytest

Note: import requests_mock library with alias (because requests_mock is a fixture in pytest tests)
See example for pytest framework, GET method and your URLs:

# test_some_module.py
import requests
import requests_mock as req_mock


def my_matcher(request):
    url = request.url
    mocked_urls = [
        "http://myapi/test/status1",
        "http://myapi/test/status2",
    ]
    return url in mocked_urls  # True or False


def test_mocking_several_urls(requests_mock):  # 'requests_mock' is fixture here
    requests_mock.get(
        req_mock.ANY,  # Mock any URL before matching
        additional_matcher=my_matcher,  # Mock only matched
        text="Some fake response for all matched URLs",
    )

    ... Do your requests ...
    # GET URL#1 -> response "Some fake response for all matched URLs"
    # GET URL#2 -> response "Some fake response for all matched URLs"
    # GET URL#N -> Raised exception 'NoMockAddress' 
hotenov
  • 911
  • 1
  • 11
  • 17