MLu
MLu

Reputation: 1390

Python "responses" mock - unable to call one URL multiple times

I'm using python responses module to mock API response in my tests. The problem is that after the url is registered responses.add() it can be called only once with requests.get(), subsequent calls for the same URL raise an exception.

import requests
import responses

URL = 'http://localhost/abcd'

# Test method
@responses.activate
def get_url(url):
  return requests.get(url)

# Set up URL mock
responses.add(responses.GET, URL, body="A B C D")

Now the first call works:

>>> print(get_url(URL))   # <Response [200]>

However the same call again fails:

>>> print(get_url(URL))

Traceback (most recent call last):
  File "test-responses.py", line 19, in <module>
    print(get_url(URL))
  File "<string>", line 3, in wrapper
  File "test-responses.py", line 10, in get_url
    return requests.get(url)
  File ".../site-packages/requests/api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File ".../site-packages/requests/api.py", line 60, in request
    return session.request(method=method, url=url, **kwargs)
  File ".../site-packages/requests/sessions.py", line 533, in request
    resp = self.send(prep, **send_kwargs)
  File ".../site-packages/requests/sessions.py", line 646, in send
    r = adapter.send(request, **kwargs)
  File ".../site-packages/responses.py", line 674, in unbound_on_send
    return self._on_request(adapter, request, *a, **kwargs)
  File ".../site-packages/responses.py", line 654, in _on_request
    raise response
requests.exceptions.ConnectionError: Connection refused by Responses - the call doesn't match any registered mock.

Request: 
- GET http://localhost/abcd

Available matches:

>>>

Is there any way to make the mock URLs permanent and call them as many times as the tests need?

Upvotes: 2

Views: 8363

Answers (2)

vvladymyrov
vvladymyrov

Reputation: 5793

The problem with your original code is that get_url wrapped with decorator and it creates a new responses context every time when get_url is called, but response.add register url mock is registered only once - for the first response context.

@response.activate decorator intended to be used for test cases. Here is correct way of using decorator that allows to make requests to same url.

@responses.activate
def test_function():
    responses.add(responses.GET, URL, body="A B C D")
    print(get_url(URL))
    print(get_url(URL))

test_function()

Using responses.start() / responses.stop() allows to make many requests for the same URL.


import requests
import responses

URL = 'http://localhost/abcd'

def get_url(url):
    return requests.get(url)

# Test method
responses.start()
# Set up URL mock
responses.add(responses.GET, URL, body="A B C D")
print(get_url(URL))
print(get_url(URL))
responses.stop()

For unit tests I'm using this mixing code .

BTW Latest responses version (0.12.0) has function assert_calls_count that allows to check the number of requests to url without exception.

assert responses.assert_call_count(URL, 1) is True

Upvotes: 4

Peaceful James
Peaceful James

Reputation: 2233

Short answer: no, I don't think so. I am looking at the source code for responses and one thing it does is raise an error for every uncalled request, so something like what you describe is incongruous with this kind of setup.

If I was facing this problem, I would make my own get function that mocks the response in advance, like this:

def mocked_get(url, body="A B C D", params=None, **kwargs):  # I stole the arguments from requests.get
    responses.add(responses.GET, url, body=body)
    return requests.get(url, params=params, **kwargs)

and I would use this instead of requests.get.

Upvotes: 1

Related Questions