Search code examples
pythonmockingpytestmonkeypatching

Changing monkeypatch setattr multiple times


I am trying to test code that depends on a third party and would like to use monkeypatch to replicate what I expect a request will return. Here is a minimal example of the code that I have.

import requests

def get_urls(*urls):
    results = []
    for url in urls:
        results.append(requests.get(url).text)

For my tests, I have something like the following:

from my_package import get_urls

def test_get_urls():
    urls = ("https://example.com/a", "https://example.com/b", "https://example.com/c")
    assert len(get_urls(urls)) == 3

How can I monkeypatch each of the calls to requests.get using monkeypatch.setattr? The mock package seems to be able to do this using side effects. How do I do this with pytest?


Solution

  • When you override a method call using monkeypatch, you can set that attribute to be a custom function. Here is one method of implementing different behaviors based on url:

    URL_MAP = {
        'https://example.com/a': json.dumps({1: 2}),
        'https://example.com/b': json.dumps({3: 4})
    }
    
    def fake_req_get(url, *args, **kwargs):
        return URL_MAP.get(url, '{}')
    
    def test_get_urls(monkeypatch):
        monkeypatch.setattr('requests.get', fake_req_get)
        urls = ("https://example.com/a", "https://example.com/b", "https://example.com/c")
        assert get_urls(urls)[0] == URL_MAP["https://example.com/a"]