Codebase list responses / 1d9abc5
New upstream version 0.12.1 Dmitry Shachnev 3 years ago
12 changed file(s) with 2372 addition(s) and 733 deletion(s). Raw diff Collapse all Expand all
0 0.12.1
1 ------
2
3 * `responses.urlencoded_params_matcher` and `responses.json_params_matcher` now
4 accept None to match empty requests.
5 * Fixed imports to work with new `urllib3` versions.
6 * `request.params` now allows parameters to have multiple values for the same key.
7 * Improved ConnectionError messages.
8
9 0.12.0
10 ------
11
12 - Remove support for Python 3.4.
13
14 0.11.0
15 ------
16
17 - Added the `match` parameter to `add()`.
18 - Added `responses.urlencoded_params_matcher()` and `responses.json_params_matcher()`.
19
20 0.10.16
21 -------
22
23 - Add a requirements pin to urllib3. This helps prevent broken install states where
24 cookie usage fails.
25
26 0.10.15
27 -------
28
29 - Added `assert_call_count` to improve ergonomics around ensuring a mock was called.
30 - Fix incorrect handling of paths with query strings.
31 - Add Python 3.9 support to CI matrix.
32
33 0.10.14
34 -------
35
36 - Retag of 0.10.13
37
38 0.10.13
39 -------
40
41 - Improved README examples.
42 - Improved handling of unicode bodies. The inferred content-type for unicode
43 bodies is now `text/plain; charset=utf-8`.
44 - Streamlined querysting matching code.
45
46 0.10.12
47 -------
48
49 - Fixed incorrect content-type in `add_callback()` when headers are provided as a list of tuples.
50
51 0.10.11
52 -------
53
54 - Fixed invalid README formatted.
55 - Fixed string formatting in error message.
56
57 0.10.10
58 ------
59
60 - Added Python 3.8 support
61 - Remove Python 3.4 from test suite matrix.
62 - The `response.request` object now has a `params` attribute that contains the query string parameters from the request that was captured.
63 - `add_passthru` now supports `re` pattern objects to match URLs.
64 - ConnectionErrors raised by responses now include more details on the request that was attempted and the mocks registered.
65
66 0.10.9
67 ------
68
69 - Fixed regression with `add_callback()` and content-type header.
70 - Fixed implicit dependency on urllib3>1.23.0
71
72 0.10.8
73 ------
74
75 - Fixed cookie parsing and enabled multiple cookies to be set by using a list of
76 tuple values.
77
78 0.10.7
79 ------
80
81 - Added pypi badges to README.
82 - Fixed formatting issues in README.
83 - Quoted cookie values are returned correctly now.
84 - Improved compatibility for pytest 5
85 - Module level method names are no longer generated dynamically improving IDE navigation.
86
87 0.10.6
88 ------
89
90 - Improved documentation.
91 - Improved installation requirements for py3
92 - ConnectionError's raised by responses now indicate which request
93 path/method failed to match a mock.
94 - `test_responses.py` is no longer part of the installation targets.
95
96 0.10.5
97 ------
98
99 - Improved support for raising exceptions from callback mocks. If a mock
100 callback returns an exception object that exception will be raised.
101
102 0.10.4
103 ------
104
105 - Fixed generated wrapper when using `@responses.activate` in Python 3.6+
106 when decorated functions use parameter and/or return annotations.
107
108 0.10.3
109 ------
110
111 - Fixed deprecation warnings in python 3.7 for inspect module usage.
112
113 0.10.2
114 ------
115
116 - Fixed build setup to use undeprecated `pytest` bin stub.
117 - Updated `tox` configuration.
118 - Added example of using responses with `pytest.fixture`
119 - Removed dependency on `biscuits` in py3. Instead `http.cookies` is being used.
120
121 0.10.1
122 ------
123
124 - Packaging fix to distribute wheel (#219)
125
126 0.10.0
127 ------
128
129 - Fix passing through extra settings (#207)
130 - Fix collections.abc warning on Python 3.7 (#215)
131 - Use 'biscuits' library instead of 'cookies' on Python 3.4+ (#218)
132
0133 0.9.0
1134 -----
2135
00 include README.rst CHANGES LICENSE
1 include test_responses.py tox.ini
12 global-exclude *~
00 Metadata-Version: 2.1
11 Name: responses
2 Version: 0.9.0
2 Version: 0.12.1
33 Summary: A utility library for mocking out the `requests` Python library.
44 Home-page: https://github.com/getsentry/responses
55 Author: David Cramer
77 Description: Responses
88 =========
99
10 .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master
11 :target: https://travis-ci.org/getsentry/responses
12
13 A utility library for mocking out the `requests` Python library.
14
15 .. note:: Responses requires Python 2.7 or newer, and requests >= 2.0
10 .. image:: https://img.shields.io/pypi/v/responses.svg
11 :target: https://pypi.python.org/pypi/responses/
12
13 .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master
14 :target: https://travis-ci.org/getsentry/responses
15
16 .. image:: https://img.shields.io/pypi/pyversions/responses.svg
17 :target: https://pypi.org/project/responses/
18
19 A utility library for mocking out the ``requests`` Python library.
20
21 .. note::
22
23 Responses requires Python 2.7 or newer, and requests >= 2.0
24
1625
1726 Installing
1827 ----------
28
1929 ``pip install responses``
30
2031
2132 Basics
2233 ------
2334
2435 The core of ``responses`` comes from registering mock responses:
2536
26 .. code-block:: python
27
28 import responses
29 import requests
37 .. code-block:: python
38
39 import responses
40 import requests
3041
3142 @responses.activate
3243 def test_simple():
4455 If you attempt to fetch a url which doesn't hit a match, ``responses`` will raise
4556 a ``ConnectionError``:
4657
47 .. code-block:: python
48
49 import responses
50 import requests
58 .. code-block:: python
59
60 import responses
61 import requests
5162
5263 from requests.exceptions import ConnectionError
5364
5869
5970 Lastly, you can pass an ``Exception`` as the body to trigger an error on the request:
6071
61 .. code-block:: python
62
63 import responses
64 import requests
72 .. code-block:: python
73
74 import responses
75 import requests
6576
6677 @responses.activate
6778 def test_simple():
7081 with pytest.raises(Exception):
7182 requests.get('http://twitter.com/api/1/foobar')
7283
84
7385 Response Parameters
7486 -------------------
7587
7688 Responses are automatically registered via params on ``add``, but can also be
7789 passed directly:
7890
79 .. code-block:: python
91 .. code-block:: python
8092
8193 import responses
8294
8496 responses.Response(
8597 method='GET',
8698 url='http://example.com',
87 ),
99 )
88100 )
89101
90102 The following attributes can be passed to a Response mock:
91103
92104 method (``str``)
93 The HTTP method (GET, POST, etc).
105 The HTTP method (GET, POST, etc).
94106
95107 url (``str`` or compiled regular expression)
96 The full resource URL.
108 The full resource URL.
97109
98110 match_querystring (``bool``)
99 Disabled by default. Include the query string when matching requests.
111 Include the query string when matching requests.
112 Enabled by default if the response URL contains a query string,
113 disabled if it doesn't or the URL is a regular expression.
100114
101115 body (``str`` or ``BufferedReader``)
102 The response body.
116 The response body.
103117
104118 json
105 A python object representing the JSON response body. Automatically configures
106 the appropriate Content-Type.
119 A Python object representing the JSON response body. Automatically configures
120 the appropriate Content-Type.
107121
108122 status (``int``)
109 The HTTP status code.
123 The HTTP status code.
110124
111125 content_type (``content_type``)
112 Defaults to ``text/plain``.
126 Defaults to ``text/plain``.
113127
114128 headers (``dict``)
115 Response headers.
129 Response headers.
116130
117131 stream (``bool``)
118 Disabled by default. Indicates the response should use the streaming API.
119
120
121
132 Disabled by default. Indicates the response should use the streaming API.
133
134 match (``list``)
135 A list of callbacks to match requests based on request body contents.
136
137
138 Matching Request Parameters
139 ---------------------------
140
141 When adding responses for endpoints that are sent request data you can add
142 matchers to ensure your code is sending the right parameters and provide
143 different responses based on the request body contents. Responses provides
144 matchers for JSON and URLencoded request bodies and you can supply your own for
145 other formats.
146
147 .. code-block:: python
148
149 import responses
150 import requests
151
152 @responses.activate
153 def test_calc_api():
154 responses.add(
155 responses.POST,
156 url='http://calc.com/sum',
157 body=4,
158 match=[
159 responses.urlencoded_params_matcher({"left": 1, "right": 3})
160 ]
161 )
162 requests.post("http://calc.com/sum", data={"left": 1, "right": 3})
163
164 Matching JSON encoded data can be done with ``responses.json_params_matcher()``.
165 If your application uses other encodings you can build your own matcher that
166 returns ``True`` or ``False`` if the request parameters match. Your matcher can
167 expect a ``request_body`` parameter to be provided by responses.
122168
123169 Dynamic Responses
124170 -----------------
126172 You can utilize callbacks to provide dynamic responses. The callback must return
127173 a tuple of (``status``, ``headers``, ``body``).
128174
129 .. code-block:: python
175 .. code-block:: python
130176
131177 import json
132178
164210 '728d329e-0e86-11e4-a748-0c84dc037c13'
165211 )
166212
213 You can also pass a compiled regex to ``add_callback`` to match multiple urls:
214
215 .. code-block:: python
216
217 import re, json
218
219 from functools import reduce
220
221 import responses
222 import requests
223
224 operators = {
225 'sum': lambda x, y: x+y,
226 'prod': lambda x, y: x*y,
227 'pow': lambda x, y: x**y
228 }
229
230 @responses.activate
231 def test_regex_url():
232
233 def request_callback(request):
234 payload = json.loads(request.body)
235 operator_name = request.path_url[1:]
236
237 operator = operators[operator_name]
238
239 resp_body = {'value': reduce(operator, payload['numbers'])}
240 headers = {'request-id': '728d329e-0e86-11e4-a748-0c84dc037c13'}
241 return (200, headers, json.dumps(resp_body))
242
243 responses.add_callback(
244 responses.POST,
245 re.compile('http://calc.com/(sum|prod|pow|unsupported)'),
246 callback=request_callback,
247 content_type='application/json',
248 )
249
250 resp = requests.post(
251 'http://calc.com/prod',
252 json.dumps({'numbers': [2, 3, 4]}),
253 headers={'content-type': 'application/json'},
254 )
255 assert resp.json() == {'value': 24}
256
257 test_regex_url()
258
259
260 If you want to pass extra keyword arguments to the callback function, for example when reusing
261 a callback function to give a slightly different result, you can use ``functools.partial``:
262
263 .. code-block:: python
264
265 from functools import partial
266
267 ...
268
269 def request_callback(request, id=None):
270 payload = json.loads(request.body)
271 resp_body = {'value': sum(payload['numbers'])}
272 headers = {'request-id': id}
273 return (200, headers, json.dumps(resp_body))
274
275 responses.add_callback(
276 responses.POST, 'http://calc.com/sum',
277 callback=partial(request_callback, id='728d329e-0e86-11e4-a748-0c84dc037c13'),
278 content_type='application/json',
279 )
280
281
282 You can see params passed in the original ``request`` in ``responses.calls[].request.params``:
283
284 .. code-block:: python
285
286 import responses
287 import requests
288
289 @responses.activate
290 def test_request_params():
291 responses.add(
292 method=responses.GET,
293 url="http://example.com?hello=world",
294 body="test",
295 match_querystring=False,
296 )
297
298 resp = requests.get('http://example.com', params={"hello": "world"})
299 assert responses.calls[0].request.params == {"hello": "world"}
167300
168301 Responses as a context manager
169302 ------------------------------
170303
171 .. code-block:: python
172
173 import responses
174 import requests
175
304 .. code-block:: python
305
306 import responses
307 import requests
176308
177309 def test_my_api():
178310 with responses.RequestsMock() as rsps:
187319 resp = requests.get('http://twitter.com/api/1/foobar')
188320 resp.status_code == 404
189321
322 Responses as a pytest fixture
323 -----------------------------
324
325 .. code-block:: python
326
327 @pytest.fixture
328 def mocked_responses():
329 with responses.RequestsMock() as rsps:
330 yield rsps
331
332 def test_api(mocked_responses):
333 mocked_responses.add(
334 responses.GET, 'http://twitter.com/api/1/foobar',
335 body='{}', status=200,
336 content_type='application/json')
337 resp = requests.get('http://twitter.com/api/1/foobar')
338 assert resp.status_code == 200
190339
191340 Assertions on declared responses
192341 --------------------------------
199348
200349 import responses
201350 import requests
202
203351
204352 def test_my_api():
205353 with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
207355 body='{}', status=200,
208356 content_type='application/json')
209357
358 assert_call_count
359 -----------------
360
361 Assert that the request was called exactly n times.
362
363 .. code-block:: python
364
365 import responses
366 import requests
367
368 @responses.activate
369 def test_assert_call_count():
370 responses.add(responses.GET, "http://example.com")
371
372 requests.get("http://example.com")
373 assert responses.assert_call_count("http://example.com", 1) is True
374
375 requests.get("http://example.com")
376 with pytest.raises(AssertionError) as excinfo:
377 responses.assert_call_count("http://example.com", 1)
378 assert "Expected URL 'http://example.com' to be called 1 times. Called 2 times." in str(excinfo.value)
379
380
210381 Multiple Responses
211382 ------------------
383
212384 You can also add multiple responses for the same url:
213385
214 .. code-block:: python
386 .. code-block:: python
215387
216388 import responses
217389 import requests
228400 resp = requests.get('http://twitter.com/api/1/foobar')
229401 assert resp.status_code == 200
230402
403
231404 Using a callback to modify the response
232405 ---------------------------------------
233406
234407 If you use customized processing in `requests` via subclassing/mixins, or if you
235408 have library tools that interact with `requests` at a low level, you may need
236 to add extended processing to the mocked Response object to fully simlulate the
409 to add extended processing to the mocked Response object to fully simulate the
237410 environment for your tests. A `response_callback` can be used, which will be
238411 wrapped by the library before being returned to the caller. The callback
239412 accepts a `response` as it's single argument, and is expected to return a
240413 single `response` object.
241414
242
243 .. code-block:: python
244
245 import responses
246 import requests
247
248 def response_callback(resp):
249 resp.callback_processed = True
250 return resp
251
252 with responses.RequestsMock(response_callback=response_callback) as m:
253 m.add(responses.GET, 'http://example.com', body=b'test')
254 resp = requests.get('http://example.com')
255 assert resp.text == "test"
256 assert hasattr(resp, 'callback_processed')
257 assert resp.callback_processed is True
258
259
260 Passing thru real requests
261 --------------------------
262
263 In some cases you may wish to allow for certain requests to pass thru responses
264 and hit a real server. This can be done with the 'passthru' methods:
415 .. code-block:: python
416
417 import responses
418 import requests
419
420 def response_callback(resp):
421 resp.callback_processed = True
422 return resp
423
424 with responses.RequestsMock(response_callback=response_callback) as m:
425 m.add(responses.GET, 'http://example.com', body=b'test')
426 resp = requests.get('http://example.com')
427 assert resp.text == "test"
428 assert hasattr(resp, 'callback_processed')
429 assert resp.callback_processed is True
430
431
432 Passing through real requests
433 -----------------------------
434
435 In some cases you may wish to allow for certain requests to pass through responses
436 and hit a real server. This can be done with the ``add_passthru`` methods:
265437
266438 .. code-block:: python
267439
273445
274446 This will allow any requests matching that prefix, that is otherwise not registered
275447 as a mock response, to passthru using the standard behavior.
448
449 Regex can be used like:
450
451 .. code-block:: python
452
453 responses.add_passthru(re.compile('https://percy.io/\\w+'))
454
455
456 Viewing/Modifying registered responses
457 --------------------------------------
458
459 Registered responses are available as a private attribute of the RequestMock
460 instance. It is sometimes useful for debugging purposes to view the stack of
461 registered responses which can be accessed via ``responses.mock._matches``.
462
463 The ``replace`` function allows a previously registered ``response`` to be
464 changed. The method signature is identical to ``add``. ``response`` s are
465 identified using ``method`` and ``url``. Only the first matched ``response`` is
466 replaced.
467
468 .. code-block:: python
469
470 import responses
471 import requests
472
473 @responses.activate
474 def test_replace():
475
476 responses.add(responses.GET, 'http://example.org', json={'data': 1})
477 responses.replace(responses.GET, 'http://example.org', json={'data': 2})
478
479 resp = requests.get('http://example.org')
480
481 assert resp.json() == {'data': 2}
482
483
484 ``remove`` takes a ``method`` and ``url`` argument and will remove **all**
485 matched responses from the registered list.
486
487 Finally, ``reset`` will reset all registered responses.
488
489 Contributing
490 ------------
491
492 Responses uses several linting and autoformatting utilities, so it's important that when
493 submitting patches you use the appropriate toolchain:
494
495 Clone the repository:
496
497 .. code-block:: shell
498
499 git clone https://github.com/getsentry/responses.git
500
501 Create an environment (e.g. with ``virtualenv``):
502
503 .. code-block:: shell
504
505 virtualenv .env && source .env/bin/activate
506
507 Configure development requirements:
508
509 .. code-block:: shell
510
511 make develop
512
513 Responses uses `Pytest <https://docs.pytest.org/en/latest/>`_ for
514 testing. You can run all tests by:
515
516 .. code-block:: shell
517
518 pytest
519
520 And run a single test by:
521
522 .. code-block:: shell
523
524 pytest -k '<test_function_name>'
276525
277526 Platform: UNKNOWN
278527 Classifier: Intended Audience :: Developers
279528 Classifier: Intended Audience :: System Administrators
280529 Classifier: Operating System :: OS Independent
530 Classifier: Programming Language :: Python
281531 Classifier: Programming Language :: Python :: 2
532 Classifier: Programming Language :: Python :: 2.7
282533 Classifier: Programming Language :: Python :: 3
534 Classifier: Programming Language :: Python :: 3.5
535 Classifier: Programming Language :: Python :: 3.6
536 Classifier: Programming Language :: Python :: 3.7
537 Classifier: Programming Language :: Python :: 3.8
538 Classifier: Programming Language :: Python :: 3.9
283539 Classifier: Topic :: Software Development
540 Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
541 Description-Content-Type: text/x-rst
284542 Provides-Extra: tests
00 Responses
11 =========
22
3 .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master
4 :target: https://travis-ci.org/getsentry/responses
5
6 A utility library for mocking out the `requests` Python library.
7
8 .. note:: Responses requires Python 2.7 or newer, and requests >= 2.0
3 .. image:: https://img.shields.io/pypi/v/responses.svg
4 :target: https://pypi.python.org/pypi/responses/
5
6 .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master
7 :target: https://travis-ci.org/getsentry/responses
8
9 .. image:: https://img.shields.io/pypi/pyversions/responses.svg
10 :target: https://pypi.org/project/responses/
11
12 A utility library for mocking out the ``requests`` Python library.
13
14 .. note::
15
16 Responses requires Python 2.7 or newer, and requests >= 2.0
17
918
1019 Installing
1120 ----------
21
1222 ``pip install responses``
23
1324
1425 Basics
1526 ------
1627
1728 The core of ``responses`` comes from registering mock responses:
1829
19 .. code-block:: python
20
21 import responses
22 import requests
30 .. code-block:: python
31
32 import responses
33 import requests
2334
2435 @responses.activate
2536 def test_simple():
3748 If you attempt to fetch a url which doesn't hit a match, ``responses`` will raise
3849 a ``ConnectionError``:
3950
40 .. code-block:: python
41
42 import responses
43 import requests
51 .. code-block:: python
52
53 import responses
54 import requests
4455
4556 from requests.exceptions import ConnectionError
4657
5162
5263 Lastly, you can pass an ``Exception`` as the body to trigger an error on the request:
5364
54 .. code-block:: python
55
56 import responses
57 import requests
65 .. code-block:: python
66
67 import responses
68 import requests
5869
5970 @responses.activate
6071 def test_simple():
6374 with pytest.raises(Exception):
6475 requests.get('http://twitter.com/api/1/foobar')
6576
77
6678 Response Parameters
6779 -------------------
6880
6981 Responses are automatically registered via params on ``add``, but can also be
7082 passed directly:
7183
72 .. code-block:: python
84 .. code-block:: python
7385
7486 import responses
7587
7789 responses.Response(
7890 method='GET',
7991 url='http://example.com',
80 ),
92 )
8193 )
8294
8395 The following attributes can be passed to a Response mock:
8496
8597 method (``str``)
86 The HTTP method (GET, POST, etc).
98 The HTTP method (GET, POST, etc).
8799
88100 url (``str`` or compiled regular expression)
89 The full resource URL.
101 The full resource URL.
90102
91103 match_querystring (``bool``)
92 Disabled by default. Include the query string when matching requests.
104 Include the query string when matching requests.
105 Enabled by default if the response URL contains a query string,
106 disabled if it doesn't or the URL is a regular expression.
93107
94108 body (``str`` or ``BufferedReader``)
95 The response body.
109 The response body.
96110
97111 json
98 A python object representing the JSON response body. Automatically configures
99 the appropriate Content-Type.
112 A Python object representing the JSON response body. Automatically configures
113 the appropriate Content-Type.
100114
101115 status (``int``)
102 The HTTP status code.
116 The HTTP status code.
103117
104118 content_type (``content_type``)
105 Defaults to ``text/plain``.
119 Defaults to ``text/plain``.
106120
107121 headers (``dict``)
108 Response headers.
122 Response headers.
109123
110124 stream (``bool``)
111 Disabled by default. Indicates the response should use the streaming API.
112
113
114
125 Disabled by default. Indicates the response should use the streaming API.
126
127 match (``list``)
128 A list of callbacks to match requests based on request body contents.
129
130
131 Matching Request Parameters
132 ---------------------------
133
134 When adding responses for endpoints that are sent request data you can add
135 matchers to ensure your code is sending the right parameters and provide
136 different responses based on the request body contents. Responses provides
137 matchers for JSON and URLencoded request bodies and you can supply your own for
138 other formats.
139
140 .. code-block:: python
141
142 import responses
143 import requests
144
145 @responses.activate
146 def test_calc_api():
147 responses.add(
148 responses.POST,
149 url='http://calc.com/sum',
150 body=4,
151 match=[
152 responses.urlencoded_params_matcher({"left": 1, "right": 3})
153 ]
154 )
155 requests.post("http://calc.com/sum", data={"left": 1, "right": 3})
156
157 Matching JSON encoded data can be done with ``responses.json_params_matcher()``.
158 If your application uses other encodings you can build your own matcher that
159 returns ``True`` or ``False`` if the request parameters match. Your matcher can
160 expect a ``request_body`` parameter to be provided by responses.
115161
116162 Dynamic Responses
117163 -----------------
119165 You can utilize callbacks to provide dynamic responses. The callback must return
120166 a tuple of (``status``, ``headers``, ``body``).
121167
122 .. code-block:: python
168 .. code-block:: python
123169
124170 import json
125171
157203 '728d329e-0e86-11e4-a748-0c84dc037c13'
158204 )
159205
206 You can also pass a compiled regex to ``add_callback`` to match multiple urls:
207
208 .. code-block:: python
209
210 import re, json
211
212 from functools import reduce
213
214 import responses
215 import requests
216
217 operators = {
218 'sum': lambda x, y: x+y,
219 'prod': lambda x, y: x*y,
220 'pow': lambda x, y: x**y
221 }
222
223 @responses.activate
224 def test_regex_url():
225
226 def request_callback(request):
227 payload = json.loads(request.body)
228 operator_name = request.path_url[1:]
229
230 operator = operators[operator_name]
231
232 resp_body = {'value': reduce(operator, payload['numbers'])}
233 headers = {'request-id': '728d329e-0e86-11e4-a748-0c84dc037c13'}
234 return (200, headers, json.dumps(resp_body))
235
236 responses.add_callback(
237 responses.POST,
238 re.compile('http://calc.com/(sum|prod|pow|unsupported)'),
239 callback=request_callback,
240 content_type='application/json',
241 )
242
243 resp = requests.post(
244 'http://calc.com/prod',
245 json.dumps({'numbers': [2, 3, 4]}),
246 headers={'content-type': 'application/json'},
247 )
248 assert resp.json() == {'value': 24}
249
250 test_regex_url()
251
252
253 If you want to pass extra keyword arguments to the callback function, for example when reusing
254 a callback function to give a slightly different result, you can use ``functools.partial``:
255
256 .. code-block:: python
257
258 from functools import partial
259
260 ...
261
262 def request_callback(request, id=None):
263 payload = json.loads(request.body)
264 resp_body = {'value': sum(payload['numbers'])}
265 headers = {'request-id': id}
266 return (200, headers, json.dumps(resp_body))
267
268 responses.add_callback(
269 responses.POST, 'http://calc.com/sum',
270 callback=partial(request_callback, id='728d329e-0e86-11e4-a748-0c84dc037c13'),
271 content_type='application/json',
272 )
273
274
275 You can see params passed in the original ``request`` in ``responses.calls[].request.params``:
276
277 .. code-block:: python
278
279 import responses
280 import requests
281
282 @responses.activate
283 def test_request_params():
284 responses.add(
285 method=responses.GET,
286 url="http://example.com?hello=world",
287 body="test",
288 match_querystring=False,
289 )
290
291 resp = requests.get('http://example.com', params={"hello": "world"})
292 assert responses.calls[0].request.params == {"hello": "world"}
160293
161294 Responses as a context manager
162295 ------------------------------
163296
164 .. code-block:: python
165
166 import responses
167 import requests
168
297 .. code-block:: python
298
299 import responses
300 import requests
169301
170302 def test_my_api():
171303 with responses.RequestsMock() as rsps:
180312 resp = requests.get('http://twitter.com/api/1/foobar')
181313 resp.status_code == 404
182314
315 Responses as a pytest fixture
316 -----------------------------
317
318 .. code-block:: python
319
320 @pytest.fixture
321 def mocked_responses():
322 with responses.RequestsMock() as rsps:
323 yield rsps
324
325 def test_api(mocked_responses):
326 mocked_responses.add(
327 responses.GET, 'http://twitter.com/api/1/foobar',
328 body='{}', status=200,
329 content_type='application/json')
330 resp = requests.get('http://twitter.com/api/1/foobar')
331 assert resp.status_code == 200
183332
184333 Assertions on declared responses
185334 --------------------------------
192341
193342 import responses
194343 import requests
195
196344
197345 def test_my_api():
198346 with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
200348 body='{}', status=200,
201349 content_type='application/json')
202350
351 assert_call_count
352 -----------------
353
354 Assert that the request was called exactly n times.
355
356 .. code-block:: python
357
358 import responses
359 import requests
360
361 @responses.activate
362 def test_assert_call_count():
363 responses.add(responses.GET, "http://example.com")
364
365 requests.get("http://example.com")
366 assert responses.assert_call_count("http://example.com", 1) is True
367
368 requests.get("http://example.com")
369 with pytest.raises(AssertionError) as excinfo:
370 responses.assert_call_count("http://example.com", 1)
371 assert "Expected URL 'http://example.com' to be called 1 times. Called 2 times." in str(excinfo.value)
372
373
203374 Multiple Responses
204375 ------------------
376
205377 You can also add multiple responses for the same url:
206378
207 .. code-block:: python
379 .. code-block:: python
208380
209381 import responses
210382 import requests
221393 resp = requests.get('http://twitter.com/api/1/foobar')
222394 assert resp.status_code == 200
223395
396
224397 Using a callback to modify the response
225398 ---------------------------------------
226399
227400 If you use customized processing in `requests` via subclassing/mixins, or if you
228401 have library tools that interact with `requests` at a low level, you may need
229 to add extended processing to the mocked Response object to fully simlulate the
402 to add extended processing to the mocked Response object to fully simulate the
230403 environment for your tests. A `response_callback` can be used, which will be
231404 wrapped by the library before being returned to the caller. The callback
232405 accepts a `response` as it's single argument, and is expected to return a
233406 single `response` object.
234407
235
236 .. code-block:: python
237
238 import responses
239 import requests
240
241 def response_callback(resp):
242 resp.callback_processed = True
243 return resp
244
245 with responses.RequestsMock(response_callback=response_callback) as m:
246 m.add(responses.GET, 'http://example.com', body=b'test')
247 resp = requests.get('http://example.com')
248 assert resp.text == "test"
249 assert hasattr(resp, 'callback_processed')
250 assert resp.callback_processed is True
251
252
253 Passing thru real requests
254 --------------------------
255
256 In some cases you may wish to allow for certain requests to pass thru responses
257 and hit a real server. This can be done with the 'passthru' methods:
408 .. code-block:: python
409
410 import responses
411 import requests
412
413 def response_callback(resp):
414 resp.callback_processed = True
415 return resp
416
417 with responses.RequestsMock(response_callback=response_callback) as m:
418 m.add(responses.GET, 'http://example.com', body=b'test')
419 resp = requests.get('http://example.com')
420 assert resp.text == "test"
421 assert hasattr(resp, 'callback_processed')
422 assert resp.callback_processed is True
423
424
425 Passing through real requests
426 -----------------------------
427
428 In some cases you may wish to allow for certain requests to pass through responses
429 and hit a real server. This can be done with the ``add_passthru`` methods:
258430
259431 .. code-block:: python
260432
266438
267439 This will allow any requests matching that prefix, that is otherwise not registered
268440 as a mock response, to passthru using the standard behavior.
441
442 Regex can be used like:
443
444 .. code-block:: python
445
446 responses.add_passthru(re.compile('https://percy.io/\\w+'))
447
448
449 Viewing/Modifying registered responses
450 --------------------------------------
451
452 Registered responses are available as a private attribute of the RequestMock
453 instance. It is sometimes useful for debugging purposes to view the stack of
454 registered responses which can be accessed via ``responses.mock._matches``.
455
456 The ``replace`` function allows a previously registered ``response`` to be
457 changed. The method signature is identical to ``add``. ``response`` s are
458 identified using ``method`` and ``url``. Only the first matched ``response`` is
459 replaced.
460
461 .. code-block:: python
462
463 import responses
464 import requests
465
466 @responses.activate
467 def test_replace():
468
469 responses.add(responses.GET, 'http://example.org', json={'data': 1})
470 responses.replace(responses.GET, 'http://example.org', json={'data': 2})
471
472 resp = requests.get('http://example.org')
473
474 assert resp.json() == {'data': 2}
475
476
477 ``remove`` takes a ``method`` and ``url`` argument and will remove **all**
478 matched responses from the registered list.
479
480 Finally, ``reset`` will reset all registered responses.
481
482 Contributing
483 ------------
484
485 Responses uses several linting and autoformatting utilities, so it's important that when
486 submitting patches you use the appropriate toolchain:
487
488 Clone the repository:
489
490 .. code-block:: shell
491
492 git clone https://github.com/getsentry/responses.git
493
494 Create an environment (e.g. with ``virtualenv``):
495
496 .. code-block:: shell
497
498 virtualenv .env && source .env/bin/activate
499
500 Configure development requirements:
501
502 .. code-block:: shell
503
504 make develop
505
506 Responses uses `Pytest <https://docs.pytest.org/en/latest/>`_ for
507 testing. You can run all tests by:
508
509 .. code-block:: shell
510
511 pytest
512
513 And run a single test by:
514
515 .. code-block:: shell
516
517 pytest -k '<test_function_name>'
00 Metadata-Version: 2.1
11 Name: responses
2 Version: 0.9.0
2 Version: 0.12.1
33 Summary: A utility library for mocking out the `requests` Python library.
44 Home-page: https://github.com/getsentry/responses
55 Author: David Cramer
77 Description: Responses
88 =========
99
10 .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master
11 :target: https://travis-ci.org/getsentry/responses
12
13 A utility library for mocking out the `requests` Python library.
14
15 .. note:: Responses requires Python 2.7 or newer, and requests >= 2.0
10 .. image:: https://img.shields.io/pypi/v/responses.svg
11 :target: https://pypi.python.org/pypi/responses/
12
13 .. image:: https://travis-ci.org/getsentry/responses.svg?branch=master
14 :target: https://travis-ci.org/getsentry/responses
15
16 .. image:: https://img.shields.io/pypi/pyversions/responses.svg
17 :target: https://pypi.org/project/responses/
18
19 A utility library for mocking out the ``requests`` Python library.
20
21 .. note::
22
23 Responses requires Python 2.7 or newer, and requests >= 2.0
24
1625
1726 Installing
1827 ----------
28
1929 ``pip install responses``
30
2031
2132 Basics
2233 ------
2334
2435 The core of ``responses`` comes from registering mock responses:
2536
26 .. code-block:: python
27
28 import responses
29 import requests
37 .. code-block:: python
38
39 import responses
40 import requests
3041
3142 @responses.activate
3243 def test_simple():
4455 If you attempt to fetch a url which doesn't hit a match, ``responses`` will raise
4556 a ``ConnectionError``:
4657
47 .. code-block:: python
48
49 import responses
50 import requests
58 .. code-block:: python
59
60 import responses
61 import requests
5162
5263 from requests.exceptions import ConnectionError
5364
5869
5970 Lastly, you can pass an ``Exception`` as the body to trigger an error on the request:
6071
61 .. code-block:: python
62
63 import responses
64 import requests
72 .. code-block:: python
73
74 import responses
75 import requests
6576
6677 @responses.activate
6778 def test_simple():
7081 with pytest.raises(Exception):
7182 requests.get('http://twitter.com/api/1/foobar')
7283
84
7385 Response Parameters
7486 -------------------
7587
7688 Responses are automatically registered via params on ``add``, but can also be
7789 passed directly:
7890
79 .. code-block:: python
91 .. code-block:: python
8092
8193 import responses
8294
8496 responses.Response(
8597 method='GET',
8698 url='http://example.com',
87 ),
99 )
88100 )
89101
90102 The following attributes can be passed to a Response mock:
91103
92104 method (``str``)
93 The HTTP method (GET, POST, etc).
105 The HTTP method (GET, POST, etc).
94106
95107 url (``str`` or compiled regular expression)
96 The full resource URL.
108 The full resource URL.
97109
98110 match_querystring (``bool``)
99 Disabled by default. Include the query string when matching requests.
111 Include the query string when matching requests.
112 Enabled by default if the response URL contains a query string,
113 disabled if it doesn't or the URL is a regular expression.
100114
101115 body (``str`` or ``BufferedReader``)
102 The response body.
116 The response body.
103117
104118 json
105 A python object representing the JSON response body. Automatically configures
106 the appropriate Content-Type.
119 A Python object representing the JSON response body. Automatically configures
120 the appropriate Content-Type.
107121
108122 status (``int``)
109 The HTTP status code.
123 The HTTP status code.
110124
111125 content_type (``content_type``)
112 Defaults to ``text/plain``.
126 Defaults to ``text/plain``.
113127
114128 headers (``dict``)
115 Response headers.
129 Response headers.
116130
117131 stream (``bool``)
118 Disabled by default. Indicates the response should use the streaming API.
119
120
121
132 Disabled by default. Indicates the response should use the streaming API.
133
134 match (``list``)
135 A list of callbacks to match requests based on request body contents.
136
137
138 Matching Request Parameters
139 ---------------------------
140
141 When adding responses for endpoints that are sent request data you can add
142 matchers to ensure your code is sending the right parameters and provide
143 different responses based on the request body contents. Responses provides
144 matchers for JSON and URLencoded request bodies and you can supply your own for
145 other formats.
146
147 .. code-block:: python
148
149 import responses
150 import requests
151
152 @responses.activate
153 def test_calc_api():
154 responses.add(
155 responses.POST,
156 url='http://calc.com/sum',
157 body=4,
158 match=[
159 responses.urlencoded_params_matcher({"left": 1, "right": 3})
160 ]
161 )
162 requests.post("http://calc.com/sum", data={"left": 1, "right": 3})
163
164 Matching JSON encoded data can be done with ``responses.json_params_matcher()``.
165 If your application uses other encodings you can build your own matcher that
166 returns ``True`` or ``False`` if the request parameters match. Your matcher can
167 expect a ``request_body`` parameter to be provided by responses.
122168
123169 Dynamic Responses
124170 -----------------
126172 You can utilize callbacks to provide dynamic responses. The callback must return
127173 a tuple of (``status``, ``headers``, ``body``).
128174
129 .. code-block:: python
175 .. code-block:: python
130176
131177 import json
132178
164210 '728d329e-0e86-11e4-a748-0c84dc037c13'
165211 )
166212
213 You can also pass a compiled regex to ``add_callback`` to match multiple urls:
214
215 .. code-block:: python
216
217 import re, json
218
219 from functools import reduce
220
221 import responses
222 import requests
223
224 operators = {
225 'sum': lambda x, y: x+y,
226 'prod': lambda x, y: x*y,
227 'pow': lambda x, y: x**y
228 }
229
230 @responses.activate
231 def test_regex_url():
232
233 def request_callback(request):
234 payload = json.loads(request.body)
235 operator_name = request.path_url[1:]
236
237 operator = operators[operator_name]
238
239 resp_body = {'value': reduce(operator, payload['numbers'])}
240 headers = {'request-id': '728d329e-0e86-11e4-a748-0c84dc037c13'}
241 return (200, headers, json.dumps(resp_body))
242
243 responses.add_callback(
244 responses.POST,
245 re.compile('http://calc.com/(sum|prod|pow|unsupported)'),
246 callback=request_callback,
247 content_type='application/json',
248 )
249
250 resp = requests.post(
251 'http://calc.com/prod',
252 json.dumps({'numbers': [2, 3, 4]}),
253 headers={'content-type': 'application/json'},
254 )
255 assert resp.json() == {'value': 24}
256
257 test_regex_url()
258
259
260 If you want to pass extra keyword arguments to the callback function, for example when reusing
261 a callback function to give a slightly different result, you can use ``functools.partial``:
262
263 .. code-block:: python
264
265 from functools import partial
266
267 ...
268
269 def request_callback(request, id=None):
270 payload = json.loads(request.body)
271 resp_body = {'value': sum(payload['numbers'])}
272 headers = {'request-id': id}
273 return (200, headers, json.dumps(resp_body))
274
275 responses.add_callback(
276 responses.POST, 'http://calc.com/sum',
277 callback=partial(request_callback, id='728d329e-0e86-11e4-a748-0c84dc037c13'),
278 content_type='application/json',
279 )
280
281
282 You can see params passed in the original ``request`` in ``responses.calls[].request.params``:
283
284 .. code-block:: python
285
286 import responses
287 import requests
288
289 @responses.activate
290 def test_request_params():
291 responses.add(
292 method=responses.GET,
293 url="http://example.com?hello=world",
294 body="test",
295 match_querystring=False,
296 )
297
298 resp = requests.get('http://example.com', params={"hello": "world"})
299 assert responses.calls[0].request.params == {"hello": "world"}
167300
168301 Responses as a context manager
169302 ------------------------------
170303
171 .. code-block:: python
172
173 import responses
174 import requests
175
304 .. code-block:: python
305
306 import responses
307 import requests
176308
177309 def test_my_api():
178310 with responses.RequestsMock() as rsps:
187319 resp = requests.get('http://twitter.com/api/1/foobar')
188320 resp.status_code == 404
189321
322 Responses as a pytest fixture
323 -----------------------------
324
325 .. code-block:: python
326
327 @pytest.fixture
328 def mocked_responses():
329 with responses.RequestsMock() as rsps:
330 yield rsps
331
332 def test_api(mocked_responses):
333 mocked_responses.add(
334 responses.GET, 'http://twitter.com/api/1/foobar',
335 body='{}', status=200,
336 content_type='application/json')
337 resp = requests.get('http://twitter.com/api/1/foobar')
338 assert resp.status_code == 200
190339
191340 Assertions on declared responses
192341 --------------------------------
199348
200349 import responses
201350 import requests
202
203351
204352 def test_my_api():
205353 with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
207355 body='{}', status=200,
208356 content_type='application/json')
209357
358 assert_call_count
359 -----------------
360
361 Assert that the request was called exactly n times.
362
363 .. code-block:: python
364
365 import responses
366 import requests
367
368 @responses.activate
369 def test_assert_call_count():
370 responses.add(responses.GET, "http://example.com")
371
372 requests.get("http://example.com")
373 assert responses.assert_call_count("http://example.com", 1) is True
374
375 requests.get("http://example.com")
376 with pytest.raises(AssertionError) as excinfo:
377 responses.assert_call_count("http://example.com", 1)
378 assert "Expected URL 'http://example.com' to be called 1 times. Called 2 times." in str(excinfo.value)
379
380
210381 Multiple Responses
211382 ------------------
383
212384 You can also add multiple responses for the same url:
213385
214 .. code-block:: python
386 .. code-block:: python
215387
216388 import responses
217389 import requests
228400 resp = requests.get('http://twitter.com/api/1/foobar')
229401 assert resp.status_code == 200
230402
403
231404 Using a callback to modify the response
232405 ---------------------------------------
233406
234407 If you use customized processing in `requests` via subclassing/mixins, or if you
235408 have library tools that interact with `requests` at a low level, you may need
236 to add extended processing to the mocked Response object to fully simlulate the
409 to add extended processing to the mocked Response object to fully simulate the
237410 environment for your tests. A `response_callback` can be used, which will be
238411 wrapped by the library before being returned to the caller. The callback
239412 accepts a `response` as it's single argument, and is expected to return a
240413 single `response` object.
241414
242
243 .. code-block:: python
244
245 import responses
246 import requests
247
248 def response_callback(resp):
249 resp.callback_processed = True
250 return resp
251
252 with responses.RequestsMock(response_callback=response_callback) as m:
253 m.add(responses.GET, 'http://example.com', body=b'test')
254 resp = requests.get('http://example.com')
255 assert resp.text == "test"
256 assert hasattr(resp, 'callback_processed')
257 assert resp.callback_processed is True
258
259
260 Passing thru real requests
261 --------------------------
262
263 In some cases you may wish to allow for certain requests to pass thru responses
264 and hit a real server. This can be done with the 'passthru' methods:
415 .. code-block:: python
416
417 import responses
418 import requests
419
420 def response_callback(resp):
421 resp.callback_processed = True
422 return resp
423
424 with responses.RequestsMock(response_callback=response_callback) as m:
425 m.add(responses.GET, 'http://example.com', body=b'test')
426 resp = requests.get('http://example.com')
427 assert resp.text == "test"
428 assert hasattr(resp, 'callback_processed')
429 assert resp.callback_processed is True
430
431
432 Passing through real requests
433 -----------------------------
434
435 In some cases you may wish to allow for certain requests to pass through responses
436 and hit a real server. This can be done with the ``add_passthru`` methods:
265437
266438 .. code-block:: python
267439
273445
274446 This will allow any requests matching that prefix, that is otherwise not registered
275447 as a mock response, to passthru using the standard behavior.
448
449 Regex can be used like:
450
451 .. code-block:: python
452
453 responses.add_passthru(re.compile('https://percy.io/\\w+'))
454
455
456 Viewing/Modifying registered responses
457 --------------------------------------
458
459 Registered responses are available as a private attribute of the RequestMock
460 instance. It is sometimes useful for debugging purposes to view the stack of
461 registered responses which can be accessed via ``responses.mock._matches``.
462
463 The ``replace`` function allows a previously registered ``response`` to be
464 changed. The method signature is identical to ``add``. ``response`` s are
465 identified using ``method`` and ``url``. Only the first matched ``response`` is
466 replaced.
467
468 .. code-block:: python
469
470 import responses
471 import requests
472
473 @responses.activate
474 def test_replace():
475
476 responses.add(responses.GET, 'http://example.org', json={'data': 1})
477 responses.replace(responses.GET, 'http://example.org', json={'data': 2})
478
479 resp = requests.get('http://example.org')
480
481 assert resp.json() == {'data': 2}
482
483
484 ``remove`` takes a ``method`` and ``url`` argument and will remove **all**
485 matched responses from the registered list.
486
487 Finally, ``reset`` will reset all registered responses.
488
489 Contributing
490 ------------
491
492 Responses uses several linting and autoformatting utilities, so it's important that when
493 submitting patches you use the appropriate toolchain:
494
495 Clone the repository:
496
497 .. code-block:: shell
498
499 git clone https://github.com/getsentry/responses.git
500
501 Create an environment (e.g. with ``virtualenv``):
502
503 .. code-block:: shell
504
505 virtualenv .env && source .env/bin/activate
506
507 Configure development requirements:
508
509 .. code-block:: shell
510
511 make develop
512
513 Responses uses `Pytest <https://docs.pytest.org/en/latest/>`_ for
514 testing. You can run all tests by:
515
516 .. code-block:: shell
517
518 pytest
519
520 And run a single test by:
521
522 .. code-block:: shell
523
524 pytest -k '<test_function_name>'
276525
277526 Platform: UNKNOWN
278527 Classifier: Intended Audience :: Developers
279528 Classifier: Intended Audience :: System Administrators
280529 Classifier: Operating System :: OS Independent
530 Classifier: Programming Language :: Python
281531 Classifier: Programming Language :: Python :: 2
532 Classifier: Programming Language :: Python :: 2.7
282533 Classifier: Programming Language :: Python :: 3
534 Classifier: Programming Language :: Python :: 3.5
535 Classifier: Programming Language :: Python :: 3.6
536 Classifier: Programming Language :: Python :: 3.7
537 Classifier: Programming Language :: Python :: 3.8
538 Classifier: Programming Language :: Python :: 3.9
283539 Classifier: Topic :: Software Development
540 Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
541 Description-Content-Type: text/x-rst
284542 Provides-Extra: tests
55 setup.cfg
66 setup.py
77 test_responses.py
8 tox.ini
89 responses.egg-info/PKG-INFO
910 responses.egg-info/SOURCES.txt
1011 responses.egg-info/dependency_links.txt
00 requests>=2.0
1 cookies
1 urllib3>=1.25.10
22 six
33
4 [:python_version in "2.6, 2.7, 3.2"]
4 [:python_version < "3.3"]
55 mock
66
7 [:python_version < "3.4"]
8 cookies
9
710 [tests]
8 pytest
9 coverage<5.0.0,>=3.7.1
11 coverage<6.0.0,>=3.7.1
1012 pytest-cov
1113 pytest-localserver
1214 flake8
15
16 [tests:python_version < "3.5"]
17 pytest<5.0,>=4.6
18
19 [tests:python_version >= "3.5"]
20 pytest>=4.6
00 responses
1 test_responses
0 from __future__ import (absolute_import, print_function, division,
1 unicode_literals)
0 from __future__ import absolute_import, print_function, division, unicode_literals
21
32 import _io
43 import inspect
54 import json as json_module
65 import logging
76 import re
7 from itertools import groupby
8
89 import six
910
10 from collections import namedtuple, Sequence, Sized
11 from collections import namedtuple
1112 from functools import update_wrapper
12 from cookies import Cookies
1313 from requests.adapters import HTTPAdapter
1414 from requests.exceptions import ConnectionError
1515 from requests.sessions import REDIRECT_STATI
1616 from requests.utils import cookiejar_from_dict
1717
1818 try:
19 from collections.abc import Sequence, Sized
20 except ImportError:
21 from collections import Sequence, Sized
22
23 try:
1924 from requests.packages.urllib3.response import HTTPResponse
2025 except ImportError:
2126 from urllib3.response import HTTPResponse
27 try:
28 from requests.packages.urllib3.connection import HTTPHeaderDict
29 except ImportError:
30 from urllib3.response import HTTPHeaderDict
2231
2332 if six.PY2:
2433 from urlparse import urlparse, parse_qsl, urlsplit, urlunsplit
4554 # Python 3.7
4655 Pattern = re.Pattern
4756
57 try:
58 from json.decoder import JSONDecodeError
59 except ImportError:
60 JSONDecodeError = ValueError
61
4862 UNSET = object()
4963
50 Call = namedtuple('Call', ['request', 'response'])
64 Call = namedtuple("Call", ["request", "response"])
5165
5266 _real_send = HTTPAdapter.send
5367
54 _wrapper_template = """\
55 def wrapper%(signature)s:
56 with responses:
57 return func%(funcargs)s
58 """
59
60 logger = logging.getLogger('responses')
68 logger = logging.getLogger("responses")
6169
6270
6371 def _is_string(s):
7381 urllist = list(urlsplit(url))
7482 netloc = urllist[1]
7583 if _has_unicode(netloc):
76 domains = netloc.split('.')
84 domains = netloc.split(".")
7785 for i, d in enumerate(domains):
7886 if _has_unicode(d):
79 d = 'xn--' + d.encode('punycode').decode('ascii')
87 d = "xn--" + d.encode("punycode").decode("ascii")
8088 domains[i] = d
81 urllist[1] = '.'.join(domains)
89 urllist[1] = ".".join(domains)
8290 url = urlunsplit(urllist)
8391
8492 # Clean up path/query/params, which use url-encoding to handle unicode chars
85 if isinstance(url.encode('utf8'), six.string_types):
86 url = url.encode('utf8')
93 if isinstance(url.encode("utf8"), six.string_types):
94 url = url.encode("utf8")
8795 chars = list(url)
8896 for i, x in enumerate(chars):
8997 if ord(x) > 128:
9098 chars[i] = quote(x)
9199
92 return ''.join(chars)
100 return "".join(chars)
93101
94102
95103 def _is_redirect(response):
96104 try:
97105 # 2.0.0 <= requests <= 2.2
98106 return response.is_redirect
107
99108 except AttributeError:
100109 # requests > 2.2
101110 return (
102111 # use request.sessions conditional
103 response.status_code in REDIRECT_STATI and
104 'location' in response.headers)
105
106
107 def get_wrapped(func, wrapper_template, evaldict):
108 # Preserve the argspec for the wrapped function so that testing
109 # tools such as pytest can continue to use their fixture injection.
112 response.status_code in REDIRECT_STATI
113 and "location" in response.headers
114 )
115
116
117 def _ensure_str(s):
118 if six.PY2:
119 s = s.encode("utf-8") if isinstance(s, six.text_type) else s
120 return s
121
122
123 def _cookies_from_headers(headers):
124 try:
125 import http.cookies as cookies
126
127 resp_cookie = cookies.SimpleCookie()
128 resp_cookie.load(headers["set-cookie"])
129
130 cookies_dict = {name: v.value for name, v in resp_cookie.items()}
131 except ImportError:
132 from cookies import Cookies
133
134 resp_cookies = Cookies.from_request(_ensure_str(headers["set-cookie"]))
135 cookies_dict = {
136 v.name: quote(_ensure_str(v.value)) for _, v in resp_cookies.items()
137 }
138 return cookiejar_from_dict(cookies_dict)
139
140
141 _wrapper_template = """\
142 def wrapper%(wrapper_args)s:
143 with responses:
144 return func%(func_args)s
145 """
146
147
148 def get_wrapped(func, responses):
110149 if six.PY2:
111150 args, a, kw, defaults = inspect.getargspec(func)
151 wrapper_args = inspect.formatargspec(args, a, kw, defaults)
152
153 # Preserve the argspec for the wrapped function so that testing
154 # tools such as pytest can continue to use their fixture injection.
155 if hasattr(func, "__self__"):
156 args = args[1:] # Omit 'self'
157 func_args = inspect.formatargspec(args, a, kw, None)
112158 else:
113 args, a, kw, defaults, kwonlyargs, kwonlydefaults, annotations = \
114 inspect.getfullargspec(func)
115
116 signature = inspect.formatargspec(args, a, kw, defaults)
117 is_bound_method = hasattr(func, '__self__')
118 if is_bound_method:
119 args = args[1:] # Omit 'self'
120 callargs = inspect.formatargspec(args, a, kw, None)
121
122 ctx = {'signature': signature, 'funcargs': callargs}
123 six.exec_(wrapper_template % ctx, evaldict)
124
125 wrapper = evaldict['wrapper']
126
159 signature = inspect.signature(func)
160 signature = signature.replace(return_annotation=inspect.Signature.empty)
161 # If the function is wrapped, switch to *args, **kwargs for the parameters
162 # as we can't rely on the signature to give us the arguments the function will
163 # be called with. For example unittest.mock.patch uses required args that are
164 # not actually passed to the function when invoked.
165 if hasattr(func, "__wrapped__"):
166 wrapper_params = [
167 inspect.Parameter("args", inspect.Parameter.VAR_POSITIONAL),
168 inspect.Parameter("kwargs", inspect.Parameter.VAR_KEYWORD),
169 ]
170 else:
171 wrapper_params = [
172 param.replace(annotation=inspect.Parameter.empty)
173 for param in signature.parameters.values()
174 ]
175 signature = signature.replace(parameters=wrapper_params)
176
177 wrapper_args = str(signature)
178 params_without_defaults = [
179 param.replace(
180 annotation=inspect.Parameter.empty, default=inspect.Parameter.empty
181 )
182 for param in signature.parameters.values()
183 ]
184 signature = signature.replace(parameters=params_without_defaults)
185 func_args = str(signature)
186
187 evaldict = {"func": func, "responses": responses}
188 six.exec_(
189 _wrapper_template % {"wrapper_args": wrapper_args, "func_args": func_args},
190 evaldict,
191 )
192 wrapper = evaldict["wrapper"]
127193 update_wrapper(wrapper, func)
128 if is_bound_method:
129 wrapper = wrapper.__get__(func.__self__, type(func.__self__))
130194 return wrapper
131195
132196
153217 def _ensure_url_default_path(url):
154218 if _is_string(url):
155219 url_parts = list(urlsplit(url))
156 if url_parts[2] == '':
157 url_parts[2] = '/'
220 if url_parts[2] == "":
221 url_parts[2] = "/"
158222 url = urlunsplit(url_parts)
159223 return url
160224
161225
162226 def _handle_body(body):
163227 if isinstance(body, six.text_type):
164 body = body.encode('utf-8')
228 body = body.encode("utf-8")
165229 if isinstance(body, _io.BufferedReader):
166230 return body
231
167232 return BufferIO(body)
233
234
235 _unspecified = object()
236
237
238 def urlencoded_params_matcher(params):
239 def match(request_body):
240 return (
241 params is None
242 if request_body is None
243 else sorted(params.items()) == sorted(parse_qsl(request_body))
244 )
245
246 return match
247
248
249 def json_params_matcher(params):
250 def match(request_body):
251 try:
252 if isinstance(request_body, bytes):
253 request_body = request_body.decode("utf-8")
254 return (
255 params is None
256 if request_body is None
257 else params == json_module.loads(request_body)
258 )
259 except JSONDecodeError:
260 return False
261
262 return match
168263
169264
170265 class BaseResponse(object):
173268
174269 stream = False
175270
176 def __init__(self, method, url, match_querystring=False):
271 def __init__(self, method, url, match_querystring=_unspecified, match=[]):
177272 self.method = method
178 self.match_querystring = match_querystring
179273 # ensure the url has a default path set if the url is a string
180274 self.url = _ensure_url_default_path(url)
275 self.match_querystring = self._should_match_querystring(match_querystring)
276 self.match = match
181277 self.call_count = 0
182278
183279 def __eq__(self, other):
187283 if self.method != other.method:
188284 return False
189285
190 # Can't simply do a equality check on the objects directly here since __eq__ isn't
286 # Can't simply do an equality check on the objects directly here since __eq__ isn't
191287 # implemented for regex. It might seem to work as regex is using a cache to return
192288 # the same regex instances, but it doesn't in all cases.
193 self_url = self.url.pattern if isinstance(self.url,
194 Pattern) else self.url
195 other_url = other.url.pattern if isinstance(other.url,
196 Pattern) else other.url
289 self_url = self.url.pattern if isinstance(self.url, Pattern) else self.url
290 other_url = other.url.pattern if isinstance(other.url, Pattern) else other.url
197291
198292 return self_url == other_url
199293
210304 url_qsl = sorted(parse_qsl(url_parsed.query))
211305 other_qsl = sorted(parse_qsl(other_parsed.query))
212306
213 if len(url_qsl) != len(other_qsl):
307 return url_qsl == other_qsl
308
309 def _should_match_querystring(self, match_querystring_argument):
310 if match_querystring_argument is not _unspecified:
311 return match_querystring_argument
312
313 if isinstance(self.url, Pattern):
314 # the old default from <= 0.9.0
214315 return False
215316
216 for (a_k, a_v), (b_k, b_v) in zip(url_qsl, other_qsl):
217 if a_k != b_k:
218 return False
219 if a_v != b_v:
220 return False
221 return True
317 return bool(urlparse(self.url).query)
222318
223319 def _url_matches(self, url, other, match_querystring=False):
224320 if _is_string(url):
225321 if _has_unicode(url):
226322 url = _clean_unicode(url)
227323 if not isinstance(other, six.text_type):
228 other = other.encode('ascii').decode('utf8')
324 other = other.encode("ascii").decode("utf8")
229325 if match_querystring:
230326 return self._url_matches_strict(url, other)
327
231328 else:
232 url_without_qs = url.split('?', 1)[0]
233 other_without_qs = other.split('?', 1)[0]
329 url_without_qs = url.split("?", 1)[0]
330 other_without_qs = other.split("?", 1)[0]
234331 return url_without_qs == other_without_qs
332
235333 elif isinstance(url, Pattern) and url.match(other):
236334 return True
335
237336 else:
238337 return False
239338
339 def _body_matches(self, match, request_body):
340 for matcher in match:
341 if not matcher(request_body):
342 return False
343
344 return True
345
240346 def get_headers(self):
241 headers = {}
347 headers = HTTPHeaderDict() # Duplicate headers are legal
242348 if self.content_type is not None:
243 headers['Content-Type'] = self.content_type
349 headers["Content-Type"] = self.content_type
244350 if self.headers:
245 headers.update(self.headers)
351 headers.extend(self.headers)
246352 return headers
247353
248354 def get_response(self, request):
250356
251357 def matches(self, request):
252358 if request.method != self.method:
253 return False
254
255 if not self._url_matches(self.url, request.url,
256 self.match_querystring):
257 return False
258
259 return True
359 return False, "Method does not match"
360
361 if not self._url_matches(self.url, request.url, self.match_querystring):
362 return False, "URL does not match"
363
364 if not self._body_matches(self.match, request.body):
365 return False, "Parameters do not match"
366
367 return True, ""
260368
261369
262370 class Response(BaseResponse):
263 def __init__(self,
264 method,
265 url,
266 body='',
267 json=None,
268 status=200,
269 headers=None,
270 stream=False,
271 content_type=UNSET,
272 **kwargs):
371 def __init__(
372 self,
373 method,
374 url,
375 body="",
376 json=None,
377 status=200,
378 headers=None,
379 stream=False,
380 content_type=UNSET,
381 **kwargs
382 ):
273383 # if we were passed a `json` argument,
274384 # override the body and content_type
275385 if json is not None:
276386 assert not body
277387 body = json_module.dumps(json)
278388 if content_type is UNSET:
279 content_type = 'application/json'
389 content_type = "application/json"
280390
281391 if content_type is UNSET:
282 content_type = 'text/plain'
283
284 # body must be bytes
285 if isinstance(body, six.text_type):
286 body = body.encode('utf-8')
392 if isinstance(body, six.text_type) and _has_unicode(body):
393 content_type = "text/plain; charset=utf-8"
394 else:
395 content_type = "text/plain"
287396
288397 self.body = body
289398 self.status = status
299408 headers = self.get_headers()
300409 status = self.status
301410 body = _handle_body(self.body)
302
303411 return HTTPResponse(
304412 status=status,
305413 reason=six.moves.http_client.responses.get(status),
306414 body=body,
307415 headers=headers,
308 preload_content=False, )
416 original_response=OriginalResponseShim(headers),
417 preload_content=False,
418 )
309419
310420
311421 class CallbackResponse(BaseResponse):
312 def __init__(self,
313 method,
314 url,
315 callback,
316 stream=False,
317 content_type='text/plain',
318 **kwargs):
422 def __init__(
423 self, method, url, callback, stream=False, content_type="text/plain", **kwargs
424 ):
319425 self.callback = callback
320426 self.stream = stream
321427 self.content_type = content_type
329435 raise result
330436
331437 status, r_headers, body = result
438 if isinstance(body, Exception):
439 raise body
440
441 # If the callback set a content-type remove the one
442 # set in add_callback() so that we don't have multiple
443 # content type values.
444 has_content_type = False
445 if isinstance(r_headers, dict) and "Content-Type" in r_headers:
446 has_content_type = True
447 elif isinstance(r_headers, list):
448 has_content_type = any(
449 [h for h in r_headers if h and h[0].lower() == "content-type"]
450 )
451 if has_content_type:
452 headers.pop("Content-Type", None)
453
332454 body = _handle_body(body)
333 headers.update(r_headers)
455 headers.extend(r_headers)
334456
335457 return HTTPResponse(
336458 status=status,
337459 reason=six.moves.http_client.responses.get(status),
338460 body=body,
339461 headers=headers,
340 preload_content=False, )
462 original_response=OriginalResponseShim(headers),
463 preload_content=False,
464 )
465
466
467 class OriginalResponseShim(object):
468 """
469 Shim for compatibility with older versions of urllib3
470
471 requests cookie handling depends on responses having a property chain of
472 `response._original_response.msg` which contains the response headers [1]
473
474 Using HTTPResponse() for this purpose causes compatibility errors with
475 urllib3<1.23.0. To avoid adding more dependencies we can use this shim.
476
477 [1]: https://github.com/psf/requests/blob/75bdc998e2d/requests/cookies.py#L125
478 """
479
480 def __init__(self, headers):
481 self.msg = headers
482
483 def isclosed(self):
484 return True
341485
342486
343487 class RequestsMock(object):
344 DELETE = 'DELETE'
345 GET = 'GET'
346 HEAD = 'HEAD'
347 OPTIONS = 'OPTIONS'
348 PATCH = 'PATCH'
349 POST = 'POST'
350 PUT = 'PUT'
488 DELETE = "DELETE"
489 GET = "GET"
490 HEAD = "HEAD"
491 OPTIONS = "OPTIONS"
492 PATCH = "PATCH"
493 POST = "POST"
494 PUT = "PUT"
351495 response_callback = None
352496
353 def __init__(self,
354 assert_all_requests_are_fired=True,
355 response_callback=None,
356 passthru_prefixes=(),
357 target='requests.adapters.HTTPAdapter.send'):
497 def __init__(
498 self,
499 assert_all_requests_are_fired=True,
500 response_callback=None,
501 passthru_prefixes=(),
502 target="requests.adapters.HTTPAdapter.send",
503 ):
358504 self._calls = CallList()
359505 self.reset()
360506 self.assert_all_requests_are_fired = assert_all_requests_are_fired
367513 self._calls.reset()
368514
369515 def add(
370 self,
371 method=None, # method or ``Response``
372 url=None,
373 body='',
374 adding_headers=None,
375 *args,
376 **kwargs):
516 self,
517 method=None, # method or ``Response``
518 url=None,
519 body="",
520 adding_headers=None,
521 *args,
522 **kwargs
523 ):
377524 """
378525 A basic request:
379526
414561 return
415562
416563 if adding_headers is not None:
417 kwargs.setdefault('headers', adding_headers)
418
419 self._matches.append(
420 Response(method=method, url=url, body=body, **kwargs))
564 kwargs.setdefault("headers", adding_headers)
565
566 self._matches.append(Response(method=method, url=url, body=body, **kwargs))
421567
422568 def add_passthru(self, prefix):
423569 """
424 Register a URL prefix to passthru any non-matching mock requests to.
570 Register a URL prefix or regex to passthru any non-matching mock requests to.
425571
426572 For example, to allow any request to 'https://example.com', but require
427573 mocks for the remainder, you would add the prefix as so:
428574
429575 >>> responses.add_passthru('https://example.com')
576
577 Regex can be used like:
578
579 >>> responses.add_passthru(re.compile('https://example.com/\\w+'))
430580 """
431 if _has_unicode(prefix):
581 if not isinstance(prefix, Pattern) and _has_unicode(prefix):
432582 prefix = _clean_unicode(prefix)
433 self.passthru_prefixes += (prefix, )
583 self.passthru_prefixes += (prefix,)
434584
435585 def remove(self, method_or_response=None, url=None):
436586 """
449599 while response in self._matches:
450600 self._matches.remove(response)
451601
452 def replace(self,
453 method_or_response=None,
454 url=None,
455 body='',
456 *args,
457 **kwargs):
602 def replace(self, method_or_response=None, url=None, body="", *args, **kwargs):
458603 """
459604 Replaces a response previously added using ``add()``. The signature
460605 is identical to ``add()``. The response is identified using ``method``
466611 if isinstance(method_or_response, BaseResponse):
467612 response = method_or_response
468613 else:
469 response = Response(
470 method=method_or_response, url=url, body=body, **kwargs)
614 response = Response(method=method_or_response, url=url, body=body, **kwargs)
471615
472616 index = self._matches.index(response)
473617 self._matches[index] = response
474618
475 def add_callback(self,
476 method,
477 url,
478 callback,
479 match_querystring=False,
480 content_type='text/plain'):
619 def add_callback(
620 self, method, url, callback, match_querystring=False, content_type="text/plain"
621 ):
481622 # ensure the url has a default path set if the url is a string
482623 # url = _ensure_url_default_path(url, match_querystring)
483624
487628 method=method,
488629 callback=callback,
489630 content_type=content_type,
490 match_querystring=match_querystring, ))
631 match_querystring=match_querystring,
632 )
633 )
491634
492635 @property
493636 def calls(self):
504647 return success
505648
506649 def activate(self, func):
507 evaldict = {'responses': self, 'func': func}
508 return get_wrapped(func, _wrapper_template, evaldict)
650 return get_wrapped(func, self)
509651
510652 def _find_match(self, request):
511653 found = None
512654 found_match = None
655 match_failed_reasons = []
513656 for i, match in enumerate(self._matches):
514 if match.matches(request):
657 match_result, reason = match.matches(request)
658 if match_result:
515659 if found is None:
516660 found = i
517661 found_match = match
518662 else:
519663 # Multiple matches found. Remove & return the first match.
520 return self._matches.pop(found)
521 return found_match
664 return self._matches.pop(found), match_failed_reasons
665 else:
666 match_failed_reasons.append(reason)
667 return found_match, match_failed_reasons
668
669 def _parse_request_params(self, url):
670 params = {}
671 for key, val in groupby(parse_qsl(urlparse(url).query), lambda kv: kv[0]):
672 values = list(map(lambda x: x[1], val))
673 if len(values) == 1:
674 values = values[0]
675 params[key] = values
676 return params
522677
523678 def _on_request(self, adapter, request, **kwargs):
524 match = self._find_match(request)
679 match, match_failed_reasons = self._find_match(request)
525680 resp_callback = self.response_callback
681 request.params = self._parse_request_params(request.path_url)
526682
527683 if match is None:
528 if request.url.startswith(self.passthru_prefixes):
529 logger.info(
530 'request.allowed-passthru', extra={
531 'url': request.url,
532 })
533 return _real_send(adapter, request)
534
535 error_msg = 'Connection refused: {0} {1}'.format(
536 request.method, request.url)
684 if any(
685 [
686 p.match(request.url)
687 if isinstance(p, Pattern)
688 else request.url.startswith(p)
689 for p in self.passthru_prefixes
690 ]
691 ):
692 logger.info("request.allowed-passthru", extra={"url": request.url})
693 return _real_send(adapter, request, **kwargs)
694
695 error_msg = (
696 "Connection refused by Responses - the call doesn't "
697 "match any registered mock.\n\n"
698 "Request: \n"
699 "- %s %s\n\n"
700 "Available matches:\n" % (request.method, request.url)
701 )
702 for i, m in enumerate(self._matches):
703 error_msg += "- {} {} {}\n".format(
704 m.method, m.url, match_failed_reasons[i]
705 )
706
537707 response = ConnectionError(error_msg)
538708 response.request = request
539709
542712 raise response
543713
544714 try:
545 response = adapter.build_response(
546 request,
547 match.get_response(request), )
548 except Exception as response:
715 response = adapter.build_response(request, match.get_response(request))
716 except BaseException as response:
549717 match.call_count += 1
550718 self._calls.add(request, response)
551719 response = resp_callback(response) if resp_callback else response
554722 if not match.stream:
555723 response.content # NOQA
556724
557 try:
558 resp_cookies = Cookies.from_request(response.headers['set-cookie'])
559 response.cookies = cookiejar_from_dict(
560 dict((v.name, v.value) for _, v in resp_cookies.items()))
561 except (KeyError, TypeError):
562 pass
563
564725 response = resp_callback(response) if resp_callback else response
565726 match.call_count += 1
566727 self._calls.add(request, response)
577738 self._patcher.stop()
578739 if not self.assert_all_requests_are_fired:
579740 return
741
580742 if not allow_assert:
581743 return
744
582745 not_called = [m for m in self._matches if m.call_count == 0]
583746 if not_called:
584747 raise AssertionError(
585 'Not all requests have been executed {0!r}'.format([(
586 match.method, match.url) for match in not_called]))
748 "Not all requests have been executed {0!r}".format(
749 [(match.method, match.url) for match in not_called]
750 )
751 )
752
753 def assert_call_count(self, url, count):
754 call_count = len(
755 [
756 1
757 for call in self.calls
758 if call.request.url == _ensure_url_default_path(url)
759 ]
760 )
761 if call_count == count:
762 return True
763 else:
764 raise AssertionError(
765 "Expected URL '{0}' to be called {1} times. Called {2} times.".format(
766 url, count, call_count
767 )
768 )
587769
588770
589771 # expose default mock namespace
590772 mock = _default_mock = RequestsMock(assert_all_requests_are_fired=False)
591 __all__ = ['CallbackResponse', 'Response', 'RequestsMock']
592 for __attr in (a for a in dir(_default_mock) if not a.startswith('_')):
593 __all__.append(__attr)
594 globals()[__attr] = getattr(_default_mock, __attr)
773 __all__ = [
774 "CallbackResponse",
775 "Response",
776 "RequestsMock",
777 # Exposed by the RequestsMock class:
778 "activate",
779 "add",
780 "add_callback",
781 "add_passthru",
782 "assert_all_requests_are_fired",
783 "assert_call_count",
784 "calls",
785 "DELETE",
786 "GET",
787 "HEAD",
788 "OPTIONS",
789 "passthru_prefixes",
790 "PATCH",
791 "POST",
792 "PUT",
793 "remove",
794 "replace",
795 "reset",
796 "response_callback",
797 "start",
798 "stop",
799 "target",
800 ]
801
802 activate = _default_mock.activate
803 add = _default_mock.add
804 add_callback = _default_mock.add_callback
805 add_passthru = _default_mock.add_passthru
806 assert_all_requests_are_fired = _default_mock.assert_all_requests_are_fired
807 assert_call_count = _default_mock.assert_call_count
808 calls = _default_mock.calls
809 DELETE = _default_mock.DELETE
810 GET = _default_mock.GET
811 HEAD = _default_mock.HEAD
812 OPTIONS = _default_mock.OPTIONS
813 passthru_prefixes = _default_mock.passthru_prefixes
814 PATCH = _default_mock.PATCH
815 POST = _default_mock.POST
816 PUT = _default_mock.PUT
817 remove = _default_mock.remove
818 replace = _default_mock.replace
819 reset = _default_mock.reset
820 response_callback = _default_mock.response_callback
821 start = _default_mock.start
822 stop = _default_mock.stop
823 target = _default_mock.target
99 """
1010
1111 import sys
12 import logging
1312
1413 from setuptools import setup
1514 from setuptools.command.test import test as TestCommand
16 import pkg_resources
1715
1816 setup_requires = []
1917
20 if 'test' in sys.argv:
21 setup_requires.append('pytest')
18 if "test" in sys.argv:
19 setup_requires.append("pytest")
2220
2321 install_requires = [
24 'requests>=2.0',
25 'cookies',
26 'six'
22 "cookies; python_version < '3.4'",
23 "mock; python_version < '3.3'",
24 "requests>=2.0",
25 "urllib3>=1.25.10",
26 "six",
2727 ]
2828
2929 tests_require = [
30 'pytest',
31 'coverage >= 3.7.1, < 5.0.0',
32 'pytest-cov',
33 'pytest-localserver',
34 'flake8',
30 "pytest>=4.6,<5.0; python_version < '3.5'",
31 "pytest>=4.6; python_version >= '3.5'",
32 "coverage >= 3.7.1, < 6.0.0",
33 "pytest-cov",
34 "pytest-localserver",
35 "flake8",
3536 ]
3637
37 extras_require = {
38 ':python_version in "2.6, 2.7, 3.2"': ['mock'],
39 'tests': tests_require,
40 }
41
42 try:
43 if 'bdist_wheel' not in sys.argv:
44 for key, value in extras_require.items():
45 if key.startswith(':') and pkg_resources.evaluate_marker(key[1:]):
46 install_requires.extend(value)
47 except Exception:
48 logging.getLogger(__name__).exception(
49 'Something went wrong calculating platform specific dependencies, so '
50 "you're getting them all!")
51 for key, value in extras_require.items():
52 if key.startswith(':'):
53 install_requires.extend(value)
38 extras_require = {"tests": tests_require}
5439
5540
5641 class PyTest(TestCommand):
5742 def finalize_options(self):
5843 TestCommand.finalize_options(self)
59 self.test_args = ['test_responses.py']
44 self.test_args = ["test_responses.py"]
6045 self.test_suite = True
6146
6247 def run_tests(self):
6348 # import here, cause outside the eggs aren't loaded
6449 import pytest
50
6551 errno = pytest.main(self.test_args)
6652 sys.exit(errno)
6753
6854
6955 setup(
70 name='responses',
71 version='0.9.0',
72 author='David Cramer',
73 description=(
74 'A utility library for mocking out the `requests` Python library.'),
75 url='https://github.com/getsentry/responses',
76 license='Apache 2.0',
77 long_description=open('README.rst').read(),
78 py_modules=['responses', 'test_responses'],
56 name="responses",
57 version="0.12.1",
58 author="David Cramer",
59 description=("A utility library for mocking out the `requests` Python library."),
60 url="https://github.com/getsentry/responses",
61 license="Apache 2.0",
62 long_description=open("README.rst").read(),
63 long_description_content_type="text/x-rst",
64 py_modules=["responses"],
7965 zip_safe=False,
66 python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
8067 install_requires=install_requires,
8168 extras_require=extras_require,
8269 tests_require=tests_require,
8370 setup_requires=setup_requires,
84 cmdclass={'test': PyTest},
71 cmdclass={"test": PyTest},
8572 include_package_data=True,
8673 classifiers=[
87 'Intended Audience :: Developers',
88 'Intended Audience :: System Administrators',
89 'Operating System :: OS Independent',
90 'Programming Language :: Python :: 2',
91 'Programming Language :: Python :: 3', 'Topic :: Software Development'
92 ], )
74 "Intended Audience :: Developers",
75 "Intended Audience :: System Administrators",
76 "Operating System :: OS Independent",
77 "Programming Language :: Python",
78 "Programming Language :: Python :: 2",
79 "Programming Language :: Python :: 2.7",
80 "Programming Language :: Python :: 3",
81 "Programming Language :: Python :: 3.5",
82 "Programming Language :: Python :: 3.6",
83 "Programming Language :: Python :: 3.7",
84 "Programming Language :: Python :: 3.8",
85 "Programming Language :: Python :: 3.9",
86 "Topic :: Software Development",
87 ],
88 )
00 # coding: utf-8
11
2 from __future__ import (absolute_import, print_function, division,
3 unicode_literals)
4
2 from __future__ import absolute_import, print_function, division, unicode_literals
3
4 import inspect
55 import re
6 import six
7 from io import BufferedReader, BytesIO
8
9 import pytest
610 import requests
711 import responses
8 import pytest
12 from requests.exceptions import ConnectionError, HTTPError
913 from responses import BaseResponse, Response
1014
11 from inspect import getargspec
12 from requests.exceptions import ConnectionError, HTTPError
15 try:
16 from mock import patch, Mock
17 except ImportError:
18 from unittest.mock import patch, Mock
1319
1420
1521 def assert_reset():
1723 assert len(responses.calls) == 0
1824
1925
20 def assert_response(resp, body=None, content_type='text/plain'):
26 def assert_response(resp, body=None, content_type="text/plain"):
2127 assert resp.status_code == 200
22 assert resp.reason == 'OK'
28 assert resp.reason == "OK"
2329 if content_type is not None:
24 assert resp.headers['Content-Type'] == content_type
30 assert resp.headers["Content-Type"] == content_type
2531 else:
26 assert 'Content-Type' not in resp.headers
32 assert "Content-Type" not in resp.headers
2733 assert resp.text == body
2834
2935
3036 def test_response():
3137 @responses.activate
3238 def run():
33 responses.add(responses.GET, 'http://example.com', body=b'test')
34 resp = requests.get('http://example.com')
35 assert_response(resp, 'test')
39 responses.add(responses.GET, "http://example.com", body=b"test")
40 resp = requests.get("http://example.com")
41 assert_response(resp, "test")
3642 assert len(responses.calls) == 1
37 assert responses.calls[0].request.url == 'http://example.com/'
38 assert responses.calls[0].response.content == b'test'
39
40 resp = requests.get('http://example.com?foo=bar')
41 assert_response(resp, 'test')
43 assert responses.calls[0].request.url == "http://example.com/"
44 assert responses.calls[0].response.content == b"test"
45
46 resp = requests.get("http://example.com?foo=bar")
47 assert_response(resp, "test")
4248 assert len(responses.calls) == 2
43 assert responses.calls[1].request.url == 'http://example.com/?foo=bar'
44 assert responses.calls[1].response.content == b'test'
49 assert responses.calls[1].request.url == "http://example.com/?foo=bar"
50 assert responses.calls[1].response.content == b"test"
51
52 run()
53 assert_reset()
54
55
56 def test_response_encoded():
57 @responses.activate
58 def run():
59 # Path contains urlencoded =/()[]
60 url = "http://example.org/foo.bar%3D%2F%28%29%5B%5D"
61 responses.add(responses.GET, url, body="it works", status=200)
62 resp = requests.get(url)
63 assert_response(resp, "it works")
4564
4665 run()
4766 assert_reset()
5170 @responses.activate
5271 def run():
5372 responses.add(
54 responses.Response(
55 method=responses.GET,
56 url='http://example.com', ))
57 resp = requests.get('http://example.com')
58 assert_response(resp, '')
73 responses.Response(method=responses.GET, url="http://example.com")
74 )
75 resp = requests.get("http://example.com")
76 assert_response(resp, "")
5977 assert len(responses.calls) == 1
60 assert responses.calls[0].request.url == 'http://example.com/'
61
62 resp = requests.get('http://example.com?foo=bar')
63 assert_response(resp, '')
78 assert responses.calls[0].request.url == "http://example.com/"
79
80 resp = requests.get("http://example.com?foo=bar")
81 assert_response(resp, "")
6482 assert len(responses.calls) == 2
65 assert responses.calls[1].request.url == 'http://example.com/?foo=bar'
66
67
68 @pytest.mark.parametrize('original,replacement',
69 [('http://example.com/two',
70 'http://example.com/two'), (Response(
71 method=responses.GET,
72 url='http://example.com/two'), Response(
73 method=responses.GET,
74 url='http://example.com/two',
75 body='testtwo')),
76 (re.compile(r'http://example\.com/two'),
77 re.compile(r'http://example\.com/two'))])
83 assert responses.calls[1].request.url == "http://example.com/?foo=bar"
84
85
86 @pytest.mark.parametrize(
87 "original,replacement",
88 [
89 ("http://example.com/two", "http://example.com/two"),
90 (
91 Response(method=responses.GET, url="http://example.com/two"),
92 Response(
93 method=responses.GET, url="http://example.com/two", body="testtwo"
94 ),
95 ),
96 (
97 re.compile(r"http://example\.com/two"),
98 re.compile(r"http://example\.com/two"),
99 ),
100 ],
101 )
78102 def test_replace(original, replacement):
79103 @responses.activate
80104 def run():
81 responses.add(responses.GET, 'http://example.com/one', body='test1')
105 responses.add(responses.GET, "http://example.com/one", body="test1")
82106
83107 if isinstance(original, BaseResponse):
84108 responses.add(original)
85109 else:
86 responses.add(responses.GET, original, body='test2')
87
88 responses.add(responses.GET, 'http://example.com/three', body='test3')
89 responses.add(
90 responses.GET,
91 re.compile(r'http://example\.com/four'),
92 body='test3')
110 responses.add(responses.GET, original, body="test2")
111
112 responses.add(responses.GET, "http://example.com/three", body="test3")
113 responses.add(
114 responses.GET, re.compile(r"http://example\.com/four"), body="test3"
115 )
93116
94117 if isinstance(replacement, BaseResponse):
95118 responses.replace(replacement)
96119 else:
97 responses.replace(responses.GET, replacement, body='testtwo')
98
99 resp = requests.get('http://example.com/two')
100 assert_response(resp, 'testtwo')
101
102 run()
103 assert_reset()
104
105
106 @pytest.mark.parametrize('original,replacement', [
107 ('http://example.com/one', re.compile(r'http://example\.com/one')),
108 (re.compile(r'http://example\.com/one'), 'http://example.com/one'),
109 ])
120 responses.replace(responses.GET, replacement, body="testtwo")
121
122 resp = requests.get("http://example.com/two")
123 assert_response(resp, "testtwo")
124
125 run()
126 assert_reset()
127
128
129 @pytest.mark.parametrize(
130 "original,replacement",
131 [
132 ("http://example.com/one", re.compile(r"http://example\.com/one")),
133 (re.compile(r"http://example\.com/one"), "http://example.com/one"),
134 ],
135 )
110136 def test_replace_error(original, replacement):
111137 @responses.activate
112138 def run():
121147 def test_remove():
122148 @responses.activate
123149 def run():
124 responses.add(responses.GET, 'http://example.com/zero')
125 responses.add(responses.GET, 'http://example.com/one')
126 responses.add(responses.GET, 'http://example.com/two')
127 responses.add(responses.GET, re.compile(r'http://example\.com/three'))
128 responses.add(responses.GET, re.compile(r'http://example\.com/four'))
150 responses.add(responses.GET, "http://example.com/zero")
151 responses.add(responses.GET, "http://example.com/one")
152 responses.add(responses.GET, "http://example.com/two")
153 responses.add(responses.GET, re.compile(r"http://example\.com/three"))
154 responses.add(responses.GET, re.compile(r"http://example\.com/four"))
129155 re.purge()
130 responses.remove(responses.GET, 'http://example.com/two')
131 responses.remove(
132 Response(method=responses.GET, url='http://example.com/zero'))
133 responses.remove(responses.GET,
134 re.compile(r'http://example\.com/four'))
156 responses.remove(responses.GET, "http://example.com/two")
157 responses.remove(Response(method=responses.GET, url="http://example.com/zero"))
158 responses.remove(responses.GET, re.compile(r"http://example\.com/four"))
135159
136160 with pytest.raises(ConnectionError):
137 requests.get('http://example.com/zero')
138 requests.get('http://example.com/one')
161 requests.get("http://example.com/zero")
162 requests.get("http://example.com/one")
139163 with pytest.raises(ConnectionError):
140 requests.get('http://example.com/two')
141 requests.get('http://example.com/three')
164 requests.get("http://example.com/two")
165 requests.get("http://example.com/three")
142166 with pytest.raises(ConnectionError):
143 requests.get('http://example.com/four')
144
145 run()
146 assert_reset()
147
148
149 @pytest.mark.parametrize('args1,kwargs1,args2,kwargs2,expected', [
150 ((responses.GET, 'a'), {}, (responses.GET, 'a'), {}, True),
151 ((responses.GET, 'a'), {}, (responses.GET, 'b'), {}, False),
152 ((responses.GET, 'a'), {}, (responses.POST, 'a'), {}, False),
153 ((responses.GET, 'a'), {
154 'match_querystring': True
155 }, (responses.GET, 'a'), {}, True),
156 ])
167 requests.get("http://example.com/four")
168
169 run()
170 assert_reset()
171
172
173 @pytest.mark.parametrize(
174 "args1,kwargs1,args2,kwargs2,expected",
175 [
176 ((responses.GET, "a"), {}, (responses.GET, "a"), {}, True),
177 ((responses.GET, "a"), {}, (responses.GET, "b"), {}, False),
178 ((responses.GET, "a"), {}, (responses.POST, "a"), {}, False),
179 (
180 (responses.GET, "a"),
181 {"match_querystring": True},
182 (responses.GET, "a"),
183 {},
184 True,
185 ),
186 ],
187 )
157188 def test_response_equality(args1, kwargs1, args2, kwargs2, expected):
158189 o1 = BaseResponse(*args1, **kwargs1)
159190 o2 = BaseResponse(*args2, **kwargs2)
162193
163194
164195 def test_response_equality_different_objects():
165 o1 = BaseResponse(method=responses.GET, url='a')
166 o2 = 'str'
196 o1 = BaseResponse(method=responses.GET, url="a")
197 o2 = "str"
167198 assert (o1 == o2) is False
168199 assert (o1 != o2) is True
169200
171202 def test_connection_error():
172203 @responses.activate
173204 def run():
174 responses.add(responses.GET, 'http://example.com')
205 responses.add(responses.GET, "http://example.com")
175206
176207 with pytest.raises(ConnectionError):
177 requests.get('http://example.com/foo')
208 requests.get("http://example.com/foo")
178209
179210 assert len(responses.calls) == 1
180 assert responses.calls[0].request.url == 'http://example.com/foo'
211 assert responses.calls[0].request.url == "http://example.com/foo"
181212 assert type(responses.calls[0].response) is ConnectionError
182213 assert responses.calls[0].response.request
183214
188219 def test_match_querystring():
189220 @responses.activate
190221 def run():
191 url = 'http://example.com?test=1&foo=bar'
192 responses.add(responses.GET, url, match_querystring=True, body=b'test')
193 resp = requests.get('http://example.com?test=1&foo=bar')
194 assert_response(resp, 'test')
195 resp = requests.get('http://example.com?foo=bar&test=1')
196 assert_response(resp, 'test')
197 resp = requests.get('http://example.com/?foo=bar&test=1')
198 assert_response(resp, 'test')
222 url = "http://example.com?test=1&foo=bar"
223 responses.add(responses.GET, url, match_querystring=True, body=b"test")
224 resp = requests.get("http://example.com?test=1&foo=bar")
225 assert_response(resp, "test")
226 resp = requests.get("http://example.com?foo=bar&test=1")
227 assert_response(resp, "test")
228 resp = requests.get("http://example.com/?foo=bar&test=1")
229 assert_response(resp, "test")
199230
200231 run()
201232 assert_reset()
205236 @responses.activate
206237 def run():
207238 responses.add(
208 responses.GET,
209 'http://example.com',
210 body=b'test',
211 match_querystring=True)
212 resp = requests.get('http://example.com')
213 assert_response(resp, 'test')
214 resp = requests.get('http://example.com/')
215 assert_response(resp, 'test')
239 responses.GET, "http://example.com", body=b"test", match_querystring=True
240 )
241 resp = requests.get("http://example.com")
242 assert_response(resp, "test")
243 resp = requests.get("http://example.com/")
244 assert_response(resp, "test")
216245 with pytest.raises(ConnectionError):
217 requests.get('http://example.com?query=foo')
246 requests.get("http://example.com?query=foo")
218247
219248 run()
220249 assert_reset()
224253 @responses.activate
225254 def run():
226255 responses.add(
227 responses.GET,
228 'http://example.com/?test=1',
229 match_querystring=True)
256 responses.GET, "http://example.com/?test=1", match_querystring=True
257 )
230258
231259 with pytest.raises(ConnectionError):
232 requests.get('http://example.com/foo/?test=2')
260 requests.get("http://example.com/foo/?test=2")
233261
234262 run()
235263 assert_reset()
243271
244272 responses.add(
245273 responses.GET,
246 re.compile(r'http://example\.com/foo/\?test=1'),
247 body='test1',
248 match_querystring=True)
249
250 resp = requests.get('http://example.com/foo/?test=1')
251 assert_response(resp, 'test1')
274 re.compile(r"http://example\.com/foo/\?test=1"),
275 body="test1",
276 match_querystring=True,
277 )
278
279 resp = requests.get("http://example.com/foo/?test=1")
280 assert_response(resp, "test1")
252281
253282 responses.add(
254283 responses.GET,
255 re.compile(r'http://example\.com/foo/\?test=2'),
256 body='test2',
257 match_querystring=False)
258
259 resp = requests.get('http://example.com/foo/?test=2')
260 assert_response(resp, 'test2')
284 re.compile(r"http://example\.com/foo/\?test=2"),
285 body="test2",
286 match_querystring=False,
287 )
288
289 resp = requests.get("http://example.com/foo/?test=2")
290 assert_response(resp, "test2")
261291
262292 run()
263293 assert_reset()
271301
272302 responses.add(
273303 responses.GET,
274 re.compile(r'http://example\.com/foo/\?test=1'),
275 match_querystring=True)
304 re.compile(r"http://example\.com/foo/\?test=1"),
305 match_querystring=True,
306 )
276307
277308 with pytest.raises(ConnectionError):
278 requests.get('http://example.com/foo/?test=3')
309 requests.get("http://example.com/foo/?test=3")
279310
280311 responses.add(
281312 responses.GET,
282 re.compile(r'http://example\.com/foo/\?test=2'),
283 match_querystring=False)
313 re.compile(r"http://example\.com/foo/\?test=2"),
314 match_querystring=False,
315 )
284316
285317 with pytest.raises(ConnectionError):
286 requests.get('http://example.com/foo/?test=4')
318 requests.get("http://example.com/foo/?test=4")
319
320 run()
321 assert_reset()
322
323
324 def test_match_querystring_auto_activates():
325 @responses.activate
326 def run():
327 responses.add(responses.GET, "http://example.com?test=1", body=b"test")
328 resp = requests.get("http://example.com?test=1")
329 assert_response(resp, "test")
330 with pytest.raises(ConnectionError):
331 requests.get("http://example.com/?test=2")
332
333 run()
334 assert_reset()
335
336
337 def test_match_querystring_missing_key():
338 @responses.activate
339 def run():
340 responses.add(responses.GET, "http://example.com?foo=1&bar=2", body=b"test")
341 with pytest.raises(ConnectionError):
342 requests.get("http://example.com/?foo=1&baz=2")
343
344 with pytest.raises(ConnectionError):
345 requests.get("http://example.com/?bar=2&fez=1")
287346
288347 run()
289348 assert_reset()
292351 def test_accept_string_body():
293352 @responses.activate
294353 def run():
295 url = 'http://example.com/'
296 responses.add(responses.GET, url, body='test')
297 resp = requests.get(url)
298 assert_response(resp, 'test')
354 url = "http://example.com/"
355 responses.add(responses.GET, url, body="test")
356 resp = requests.get(url)
357 assert_response(resp, "test")
299358
300359 run()
301360 assert_reset()
304363 def test_accept_json_body():
305364 @responses.activate
306365 def run():
307 content_type = 'application/json'
308
309 url = 'http://example.com/'
366 content_type = "application/json"
367
368 url = "http://example.com/"
310369 responses.add(responses.GET, url, json={"message": "success"})
311370 resp = requests.get(url)
312371 assert_response(resp, '{"message": "success"}', content_type)
313372
314 url = 'http://example.com/1/'
373 url = "http://example.com/1/"
315374 responses.add(responses.GET, url, json=[])
316375 resp = requests.get(url)
317 assert_response(resp, '[]', content_type)
376 assert_response(resp, "[]", content_type)
318377
319378 run()
320379 assert_reset()
323382 def test_no_content_type():
324383 @responses.activate
325384 def run():
326 url = 'http://example.com/'
327 responses.add(responses.GET, url, body='test', content_type=None)
328 resp = requests.get(url)
329 assert_response(resp, 'test', content_type=None)
385 url = "http://example.com/"
386 responses.add(responses.GET, url, body="test", content_type=None)
387 resp = requests.get(url)
388 assert_response(resp, "test", content_type=None)
330389
331390 run()
332391 assert_reset()
335394 def test_arbitrary_status_code():
336395 @responses.activate
337396 def run():
338 url = 'http://example.com/'
339 responses.add(responses.GET, url, body='test', status=418)
340 resp = requests.get(url)
341 assert resp.status_code == 418
397 url = "http://example.com/"
398 responses.add(responses.GET, url, body="test", status=419)
399 resp = requests.get(url)
400 assert resp.status_code == 419
342401 assert resp.reason is None
343402
344403 run()
348407 def test_throw_connection_error_explicit():
349408 @responses.activate
350409 def run():
351 url = 'http://example.com'
352 exception = HTTPError('HTTP Error')
410 url = "http://example.com"
411 exception = HTTPError("HTTP Error")
353412 responses.add(responses.GET, url, exception)
354413
355414 with pytest.raises(HTTPError) as HE:
356415 requests.get(url)
357416
358 assert str(HE.value) == 'HTTP Error'
417 assert str(HE.value) == "HTTP Error"
359418
360419 run()
361420 assert_reset()
362421
363422
364423 def test_callback():
365 body = b'test callback'
424 body = b"test callback"
366425 status = 400
367 reason = 'Bad Request'
368 headers = {'foo': 'bar'}
369 url = 'http://example.com/'
426 reason = "Bad Request"
427 headers = {
428 "foo": "bar",
429 "Content-Type": "application/json",
430 "Content-Length": "13",
431 }
432 url = "http://example.com/"
370433
371434 def request_callback(request):
372435 return (status, headers, body)
378441 assert resp.text == "test callback"
379442 assert resp.status_code == status
380443 assert resp.reason == reason
381 assert 'foo' in resp.headers
382 assert resp.headers['foo'] == 'bar'
444 assert "bar" == resp.headers.get("foo")
445 assert "application/json" == resp.headers.get("Content-Type")
446 assert "13" == resp.headers.get("Content-Length")
447
448 run()
449 assert_reset()
450
451
452 def test_callback_exception_result():
453 result = Exception()
454 url = "http://example.com/"
455
456 def request_callback(request):
457 return result
458
459 @responses.activate
460 def run():
461 responses.add_callback(responses.GET, url, request_callback)
462
463 with pytest.raises(Exception) as e:
464 requests.get(url)
465
466 assert e.value is result
467
468 run()
469 assert_reset()
470
471
472 def test_callback_exception_body():
473 body = Exception()
474 url = "http://example.com/"
475
476 def request_callback(request):
477 return (200, {}, body)
478
479 @responses.activate
480 def run():
481 responses.add_callback(responses.GET, url, request_callback)
482
483 with pytest.raises(Exception) as e:
484 requests.get(url)
485
486 assert e.value is body
383487
384488 run()
385489 assert_reset()
386490
387491
388492 def test_callback_no_content_type():
389 body = b'test callback'
493 body = b"test callback"
390494 status = 400
391 reason = 'Bad Request'
392 headers = {'foo': 'bar'}
393 url = 'http://example.com/'
495 reason = "Bad Request"
496 headers = {"foo": "bar"}
497 url = "http://example.com/"
394498
395499 def request_callback(request):
396500 return (status, headers, body)
397501
398502 @responses.activate
399503 def run():
400 responses.add_callback(
401 responses.GET, url, request_callback, content_type=None)
504 responses.add_callback(responses.GET, url, request_callback, content_type=None)
402505 resp = requests.get(url)
403506 assert resp.text == "test callback"
404507 assert resp.status_code == status
405508 assert resp.reason == reason
406 assert 'foo' in resp.headers
407 assert 'Content-Type' not in resp.headers
509 assert "foo" in resp.headers
510 assert "Content-Type" not in resp.headers
511
512 run()
513 assert_reset()
514
515
516 def test_callback_content_type_dict():
517 def request_callback(request):
518 return (
519 200,
520 {"Content-Type": "application/json"},
521 b"foo",
522 )
523
524 @responses.activate
525 def run():
526 responses.add_callback("GET", "http://mockhost/.foo", callback=request_callback)
527 resp = requests.get("http://mockhost/.foo")
528 assert resp.text == "foo"
529 assert resp.headers["content-type"] == "application/json"
530
531 run()
532 assert_reset()
533
534
535 def test_callback_content_type_tuple():
536 def request_callback(request):
537 return (
538 200,
539 [("Content-Type", "application/json")],
540 b"foo",
541 )
542
543 @responses.activate
544 def run():
545 responses.add_callback("GET", "http://mockhost/.foo", callback=request_callback)
546 resp = requests.get("http://mockhost/.foo")
547 assert resp.text == "foo"
548 assert resp.headers["content-type"] == "application/json"
408549
409550 run()
410551 assert_reset()
413554 def test_regular_expression_url():
414555 @responses.activate
415556 def run():
416 url = re.compile(r'https?://(.*\.)?example.com')
417 responses.add(responses.GET, url, body=b'test')
418
419 resp = requests.get('http://example.com')
420 assert_response(resp, 'test')
421
422 resp = requests.get('https://example.com')
423 assert_response(resp, 'test')
424
425 resp = requests.get('https://uk.example.com')
426 assert_response(resp, 'test')
557 url = re.compile(r"https?://(.*\.)?example.com")
558 responses.add(responses.GET, url, body=b"test")
559
560 resp = requests.get("http://example.com")
561 assert_response(resp, "test")
562
563 resp = requests.get("https://example.com")
564 assert_response(resp, "test")
565
566 resp = requests.get("https://uk.example.com")
567 assert_response(resp, "test")
427568
428569 with pytest.raises(ConnectionError):
429 requests.get('https://uk.exaaample.com')
570 requests.get("https://uk.exaaample.com")
430571
431572 run()
432573 assert_reset()
436577 @responses.activate
437578 def run():
438579 url = "http://example.com"
439 responses.add(responses.GET, url, body=b'test')
580 responses.add(responses.GET, url, body=b"test")
440581
441582 calls = [0]
442583
457598 session.mount("http://", DummyAdapter())
458599
459600 resp = session.get(url)
460 assert_response(resp, 'test')
601 assert_response(resp, "test")
461602
462603 run()
463604
465606 def test_responses_as_context_manager():
466607 def run():
467608 with responses.mock:
468 responses.add(responses.GET, 'http://example.com', body=b'test')
469 resp = requests.get('http://example.com')
470 assert_response(resp, 'test')
609 responses.add(responses.GET, "http://example.com", body=b"test")
610 resp = requests.get("http://example.com")
611 assert_response(resp, "test")
471612 assert len(responses.calls) == 1
472 assert responses.calls[0].request.url == 'http://example.com/'
473 assert responses.calls[0].response.content == b'test'
474
475 resp = requests.get('http://example.com?foo=bar')
476 assert_response(resp, 'test')
613 assert responses.calls[0].request.url == "http://example.com/"
614 assert responses.calls[0].response.content == b"test"
615
616 resp = requests.get("http://example.com?foo=bar")
617 assert_response(resp, "test")
477618 assert len(responses.calls) == 2
478 assert (responses.calls[1].request.url ==
479 'http://example.com/?foo=bar')
480 assert responses.calls[1].response.content == b'test'
619 assert responses.calls[1].request.url == "http://example.com/?foo=bar"
620 assert responses.calls[1].response.content == b"test"
481621
482622 run()
483623 assert_reset()
488628 return (a, b)
489629
490630 decorated_test_function = responses.activate(test_function)
491 assert getargspec(test_function) == getargspec(decorated_test_function)
631 if hasattr(inspect, "signature"):
632 assert inspect.signature(test_function) == inspect.signature(
633 decorated_test_function
634 )
635 else:
636 assert inspect.getargspec(test_function) == inspect.getargspec(
637 decorated_test_function
638 )
639 assert decorated_test_function(1, 2) == test_function(1, 2)
640 assert decorated_test_function(3) == test_function(3)
641
642
643 def test_activate_mock_interaction():
644 @patch("sys.stdout")
645 def test_function(mock_stdout):
646 return mock_stdout
647
648 decorated_test_function = responses.activate(test_function)
649 if hasattr(inspect, "signature"):
650 assert inspect.signature(test_function) == inspect.signature(
651 decorated_test_function
652 )
653 else:
654 assert inspect.getargspec(test_function) == inspect.getargspec(
655 decorated_test_function
656 )
657
658 value = test_function()
659 assert isinstance(value, Mock)
660
661 value = decorated_test_function()
662 assert isinstance(value, Mock)
663
664
665 @pytest.mark.skipif(six.PY2, reason="Cannot run in python2")
666 def test_activate_doesnt_change_signature_with_return_type():
667 def test_function(a, b=None):
668 return (a, b)
669
670 # Add type annotations as they are syntax errors in py2.
671 # Use a class to test for import errors in evaled code.
672 test_function.__annotations__["return"] = Mock
673 test_function.__annotations__["a"] = Mock
674
675 decorated_test_function = responses.activate(test_function)
676 if hasattr(inspect, "signature"):
677 assert inspect.signature(test_function) == inspect.signature(
678 decorated_test_function
679 )
680 else:
681 assert inspect.getargspec(test_function) == inspect.getargspec(
682 decorated_test_function
683 )
492684 assert decorated_test_function(1, 2) == test_function(1, 2)
493685 assert decorated_test_function(3) == test_function(3)
494686
498690 def test_function(self, a, b=None):
499691 return (self, a, b)
500692
693 decorated_test_function = responses.activate(test_function)
694
501695 test_case = TestCase()
502 argspec = getargspec(test_case.test_function)
503 decorated_test_function = responses.activate(test_case.test_function)
504 assert argspec == getargspec(decorated_test_function)
505 assert decorated_test_function(1, 2) == test_case.test_function(1, 2)
506 assert decorated_test_function(3) == test_case.test_function(3)
696 assert test_case.decorated_test_function(1, 2) == test_case.test_function(1, 2)
697 assert test_case.decorated_test_function(3) == test_case.test_function(3)
507698
508699
509700 def test_response_cookies():
510 body = b'test callback'
701 body = b"test callback"
511702 status = 200
512 headers = {'set-cookie': 'session_id=12345; a=b; c=d'}
513 url = 'http://example.com/'
703 headers = {"set-cookie": "session_id=12345; a=b; c=d"}
704 url = "http://example.com/"
514705
515706 def request_callback(request):
516707 return (status, headers, body)
521712 resp = requests.get(url)
522713 assert resp.text == "test callback"
523714 assert resp.status_code == status
524 assert 'session_id' in resp.cookies
525 assert resp.cookies['session_id'] == '12345'
526 assert resp.cookies['a'] == 'b'
527 assert resp.cookies['c'] == 'd'
715 assert "session_id" in resp.cookies
716 assert resp.cookies["session_id"] == "12345"
717 assert set(resp.cookies.keys()) == set(["session_id"])
718
719 run()
720 assert_reset()
721
722
723 def test_response_secure_cookies():
724 body = b"test callback"
725 status = 200
726 headers = {"set-cookie": "session_id=12345; a=b; c=d; secure"}
727 url = "http://example.com/"
728
729 def request_callback(request):
730 return (status, headers, body)
731
732 @responses.activate
733 def run():
734 responses.add_callback(responses.GET, url, request_callback)
735 resp = requests.get(url)
736 assert resp.text == "test callback"
737 assert resp.status_code == status
738 assert "session_id" in resp.cookies
739 assert resp.cookies["session_id"] == "12345"
740 assert set(resp.cookies.keys()) == set(["session_id"])
741
742 run()
743 assert_reset()
744
745
746 def test_response_cookies_multiple():
747 body = b"test callback"
748 status = 200
749 headers = [
750 ("set-cookie", "1P_JAR=2019-12-31-23; path=/; domain=.example.com; HttpOnly"),
751 ("set-cookie", "NID=some=value; path=/; domain=.example.com; secure"),
752 ]
753 url = "http://example.com/"
754
755 def request_callback(request):
756 return (status, headers, body)
757
758 @responses.activate
759 def run():
760 responses.add_callback(responses.GET, url, request_callback)
761 resp = requests.get(url)
762 assert resp.text == "test callback"
763 assert resp.status_code == status
764 assert set(resp.cookies.keys()) == set(["1P_JAR", "NID"])
765 assert resp.cookies["1P_JAR"] == "2019-12-31-23"
766 assert resp.cookies["NID"] == "some=value"
528767
529768 run()
530769 assert_reset()
539778 return resp
540779
541780 with responses.RequestsMock(response_callback=response_callback) as m:
542 m.add(responses.GET, 'http://example.com', body=b'test')
543 resp = requests.get('http://example.com')
781 m.add(responses.GET, "http://example.com", body=b"test")
782 resp = requests.get("http://example.com")
544783 assert resp.text == "test"
545 assert hasattr(resp, '_is_mocked')
784 assert hasattr(resp, "_is_mocked")
546785 assert resp._is_mocked is True
547786
548787 run()
554793
555794 def run():
556795 with responses.RequestsMock() as m:
557 with open('README.rst', 'rb') as out:
558 m.add(
559 responses.GET, 'http://example.com', body=out, stream=True)
560 resp = requests.get('http://example.com')
561 with open('README.rst', 'r') as out:
796 with open("README.rst", "rb") as out:
797 m.add(responses.GET, "http://example.com", body=out, stream=True)
798 resp = requests.get("http://example.com")
799 with open("README.rst", "r") as out:
562800 assert resp.text == out.read()
563801
564802
565803 def test_assert_all_requests_are_fired():
804 def request_callback(request):
805 raise BaseException()
806
566807 def run():
567808 with pytest.raises(AssertionError) as excinfo:
568 with responses.RequestsMock(
569 assert_all_requests_are_fired=True) as m:
570 m.add(responses.GET, 'http://example.com', body=b'test')
571 assert 'http://example.com' in str(excinfo.value)
572 assert responses.GET in str(excinfo)
809 with responses.RequestsMock(assert_all_requests_are_fired=True) as m:
810 m.add(responses.GET, "http://example.com", body=b"test")
811 assert "http://example.com" in str(excinfo.value)
812 assert responses.GET in str(excinfo.value)
573813
574814 # check that assert_all_requests_are_fired default to True
575815 with pytest.raises(AssertionError):
576816 with responses.RequestsMock() as m:
577 m.add(responses.GET, 'http://example.com', body=b'test')
817 m.add(responses.GET, "http://example.com", body=b"test")
578818
579819 # check that assert_all_requests_are_fired doesn't swallow exceptions
580820 with pytest.raises(ValueError):
581821 with responses.RequestsMock() as m:
582 m.add(responses.GET, 'http://example.com', body=b'test')
822 m.add(responses.GET, "http://example.com", body=b"test")
583823 raise ValueError()
584824
585825 # check that assert_all_requests_are_fired=True doesn't remove urls
586826 with responses.RequestsMock(assert_all_requests_are_fired=True) as m:
587 m.add(responses.GET, 'http://example.com', body=b'test')
827 m.add(responses.GET, "http://example.com", body=b"test")
588828 assert len(m._matches) == 1
589 requests.get('http://example.com')
829 requests.get("http://example.com")
590830 assert len(m._matches) == 1
591831
592832 # check that assert_all_requests_are_fired=True counts mocked errors
593833 with responses.RequestsMock(assert_all_requests_are_fired=True) as m:
594 m.add(responses.GET, 'http://example.com', body=Exception())
834 m.add(responses.GET, "http://example.com", body=Exception())
595835 assert len(m._matches) == 1
596836 with pytest.raises(Exception):
597 requests.get('http://example.com')
837 requests.get("http://example.com")
598838 assert len(m._matches) == 1
599839
840 with responses.RequestsMock(assert_all_requests_are_fired=True) as m:
841 m.add_callback(responses.GET, "http://example.com", request_callback)
842 assert len(m._matches) == 1
843 with pytest.raises(BaseException):
844 requests.get("http://example.com")
845 assert len(m._matches) == 1
846
600847 run()
601848 assert_reset()
602849
603850
604851 def test_allow_redirects_samehost():
605 redirecting_url = 'http://example.com'
606 final_url_path = '/1'
607 final_url = '{0}{1}'.format(redirecting_url, final_url_path)
608 url_re = re.compile(r'^http://example.com(/)?(\d+)?$')
852 redirecting_url = "http://example.com"
853 final_url_path = "/1"
854 final_url = "{0}{1}".format(redirecting_url, final_url_path)
855 url_re = re.compile(r"^http://example.com(/)?(\d+)?$")
609856
610857 def request_callback(request):
611858 # endpoint of chained redirect
612859 if request.url.endswith(final_url_path):
613 return 200, (), b'test'
860 return 200, (), b"test"
861
614862 # otherwise redirect to an integer path
615863 else:
616 if request.url.endswith('/0'):
864 if request.url.endswith("/0"):
617865 n = 1
618866 else:
619867 n = 0
620 redirect_headers = {'location': '/{0!s}'.format(n)}
868 redirect_headers = {"location": "/{0!s}".format(n)}
621869 return 301, redirect_headers, None
622870
623871 def run():
624872 # setup redirect
625873 with responses.mock:
626874 responses.add_callback(responses.GET, url_re, request_callback)
627 resp_no_redirects = requests.get(
628 redirecting_url, allow_redirects=False)
875 resp_no_redirects = requests.get(redirecting_url, allow_redirects=False)
629876 assert resp_no_redirects.status_code == 301
630877 assert len(responses.calls) == 1 # 1x300
631878 assert responses.calls[0][1].status_code == 301
633880
634881 with responses.mock:
635882 responses.add_callback(responses.GET, url_re, request_callback)
636 resp_yes_redirects = requests.get(
637 redirecting_url, allow_redirects=True)
883 resp_yes_redirects = requests.get(redirecting_url, allow_redirects=True)
638884 assert len(responses.calls) == 3 # 2x300 + 1x200
639885 assert len(resp_yes_redirects.history) == 2
640886 assert resp_yes_redirects.status_code == 200
648894
649895
650896 def test_handles_unicode_querystring():
651 url = u'http://example.com/test?type=2&ie=utf8&query=汉字'
652
653 @responses.activate
654 def run():
655 responses.add(responses.GET, url, body='test', match_querystring=True)
656
657 resp = requests.get(url)
658
659 assert_response(resp, 'test')
897 url = "http://example.com/test?type=2&ie=utf8&query=汉字"
898
899 @responses.activate
900 def run():
901 responses.add(responses.GET, url, body="test", match_querystring=True)
902
903 resp = requests.get(url)
904
905 assert_response(resp, "test")
660906
661907 run()
662908 assert_reset()
663909
664910
665911 def test_handles_unicode_url():
666 url = u'http://www.संजाल.भारत/hi/वेबसाइट-डिजाइन'
667
668 @responses.activate
669 def run():
670 responses.add(responses.GET, url, body='test')
671
672 resp = requests.get(url)
673
674 assert_response(resp, 'test')
912 url = "http://www.संजाल.भारत/hi/वेबसाइट-डिजाइन"
913
914 @responses.activate
915 def run():
916 responses.add(responses.GET, url, body="test")
917
918 resp = requests.get(url)
919
920 assert_response(resp, "test")
921
922 run()
923 assert_reset()
924
925
926 def test_handles_unicode_body():
927 url = "http://example.com/test"
928
929 @responses.activate
930 def run():
931 responses.add(responses.GET, url, body="михољско лето")
932
933 resp = requests.get(url)
934
935 assert_response(resp, "михољско лето", content_type="text/plain; charset=utf-8")
936
937 run()
938 assert_reset()
939
940
941 def test_handles_buffered_reader_body():
942 url = "http://example.com/test"
943
944 @responses.activate
945 def run():
946 responses.add(responses.GET, url, body=BufferedReader(BytesIO(b"test")))
947
948 resp = requests.get(url)
949
950 assert_response(resp, "test")
675951
676952 run()
677953 assert_reset()
681957 @responses.activate
682958 def run():
683959 responses.add(
960 responses.GET, "http://example.com", body="", headers={"X-Test": "foo"}
961 )
962 resp = requests.get("http://example.com")
963 assert resp.headers["X-Test"] == "foo"
964
965 run()
966 assert_reset()
967
968
969 def test_legacy_adding_headers():
970 @responses.activate
971 def run():
972 responses.add(
684973 responses.GET,
685 'http://example.com',
686 body='',
687 headers={
688 'X-Test': 'foo',
689 })
690 resp = requests.get('http://example.com')
691 assert resp.headers['X-Test'] == 'foo'
692
693 run()
694 assert_reset()
695
696
697 def test_legacy_adding_headers():
698 @responses.activate
699 def run():
700 responses.add(
701 responses.GET,
702 'http://example.com',
703 body='',
704 adding_headers={
705 'X-Test': 'foo',
706 })
707 resp = requests.get('http://example.com')
708 assert resp.headers['X-Test'] == 'foo'
974 "http://example.com",
975 body="",
976 adding_headers={"X-Test": "foo"},
977 )
978 resp = requests.get("http://example.com")
979 assert resp.headers["X-Test"] == "foo"
709980
710981 run()
711982 assert_reset()
714985 def test_multiple_responses():
715986 @responses.activate
716987 def run():
717 responses.add(responses.GET, 'http://example.com', body='test')
718 responses.add(responses.GET, 'http://example.com', body='rest')
719
720 resp = requests.get('http://example.com')
721 assert_response(resp, 'test')
722 resp = requests.get('http://example.com')
723 assert_response(resp, 'rest')
988 responses.add(responses.GET, "http://example.com", body="test")
989 responses.add(responses.GET, "http://example.com", body="rest")
990
991 resp = requests.get("http://example.com")
992 assert_response(resp, "test")
993 resp = requests.get("http://example.com")
994 assert_response(resp, "rest")
724995 # After all responses are used, last response should be repeated
725 resp = requests.get('http://example.com')
726 assert_response(resp, 'rest')
996 resp = requests.get("http://example.com")
997 assert_response(resp, "rest")
727998
728999 run()
7291000 assert_reset()
7321003 def test_multiple_urls():
7331004 @responses.activate
7341005 def run():
735 responses.add(responses.GET, 'http://example.com/one', body='one')
736 responses.add(responses.GET, 'http://example.com/two', body='two')
737
738 resp = requests.get('http://example.com/two')
739 assert_response(resp, 'two')
740 resp = requests.get('http://example.com/one')
741 assert_response(resp, 'one')
1006 responses.add(responses.GET, "http://example.com/one", body="one")
1007 responses.add(responses.GET, "http://example.com/two", body="two")
1008
1009 resp = requests.get("http://example.com/two")
1010 assert_response(resp, "two")
1011 resp = requests.get("http://example.com/one")
1012 assert_response(resp, "one")
1013
1014 run()
1015 assert_reset()
1016
1017
1018 def test_multiple_methods():
1019 @responses.activate
1020 def run():
1021 responses.add(responses.GET, "http://example.com/one", body="gotcha")
1022 responses.add(responses.POST, "http://example.com/one", body="posted")
1023
1024 resp = requests.get("http://example.com/one")
1025 assert_response(resp, "gotcha")
1026 resp = requests.post("http://example.com/one")
1027 assert_response(resp, "posted")
7421028
7431029 run()
7441030 assert_reset()
7451031
7461032
7471033 def test_passthru(httpserver):
748 httpserver.serve_content('OK', headers={'Content-Type': 'text/plain'})
1034 httpserver.serve_content("OK", headers={"Content-Type": "text/plain"})
7491035
7501036 @responses.activate
7511037 def run():
7521038 responses.add_passthru(httpserver.url)
753 responses.add(
754 responses.GET, '{}/one'.format(httpserver.url), body='one')
755 responses.add(responses.GET, 'http://example.com/two', body='two')
756
757 resp = requests.get('http://example.com/two')
758 assert_response(resp, 'two')
759 resp = requests.get('{}/one'.format(httpserver.url))
760 assert_response(resp, 'one')
1039 responses.add(responses.GET, "{}/one".format(httpserver.url), body="one")
1040 responses.add(responses.GET, "http://example.com/two", body="two")
1041
1042 resp = requests.get("http://example.com/two")
1043 assert_response(resp, "two")
1044 resp = requests.get("{}/one".format(httpserver.url))
1045 assert_response(resp, "one")
7611046 resp = requests.get(httpserver.url)
762 assert_response(resp, 'OK')
1047 assert_response(resp, "OK")
1048
1049 run()
1050 assert_reset()
1051
1052
1053 def test_passthru_regex(httpserver):
1054 httpserver.serve_content("OK", headers={"Content-Type": "text/plain"})
1055
1056 @responses.activate
1057 def run():
1058 responses.add_passthru(re.compile("{}/\\w+".format(httpserver.url)))
1059 responses.add(responses.GET, "{}/one".format(httpserver.url), body="one")
1060 responses.add(responses.GET, "http://example.com/two", body="two")
1061
1062 resp = requests.get("http://example.com/two")
1063 assert_response(resp, "two")
1064 resp = requests.get("{}/one".format(httpserver.url))
1065 assert_response(resp, "one")
1066 resp = requests.get("{}/two".format(httpserver.url))
1067 assert_response(resp, "OK")
1068 resp = requests.get("{}/three".format(httpserver.url))
1069 assert_response(resp, "OK")
7631070
7641071 run()
7651072 assert_reset()
7681075 def test_method_named_param():
7691076 @responses.activate
7701077 def run():
771 responses.add(
772 method=responses.GET, url='http://example.com', body='OK')
773 resp = requests.get('http://example.com')
774 assert_response(resp, 'OK')
1078 responses.add(method=responses.GET, url="http://example.com", body="OK")
1079 resp = requests.get("http://example.com")
1080 assert_response(resp, "OK")
7751081
7761082 run()
7771083 assert_reset()
7811087 @responses.activate
7821088 def run():
7831089 with responses.RequestsMock() as m:
784 url = u'http://موقع.وزارة-الاتصالات.مصر/'
785 clean_url = 'http://xn--4gbrim.xn----ymcbaaajlc6dj7bxne2c.xn--wgbh1c/'
1090 url = "http://موقع.وزارة-الاتصالات.مصر/"
1091 clean_url = "http://xn--4gbrim.xn----ymcbaaajlc6dj7bxne2c.xn--wgbh1c/"
7861092 m.add_passthru(url)
7871093 assert m.passthru_prefixes[0] == clean_url
7881094
7911097
7921098
7931099 def test_custom_target(monkeypatch):
794 requests_mock = responses.RequestsMock(target='something.else')
1100 requests_mock = responses.RequestsMock(target="something.else")
7951101 std_mock_mock = responses.std_mock.MagicMock()
7961102 patch_mock = std_mock_mock.patch
797 monkeypatch.setattr(responses, 'std_mock', std_mock_mock)
1103 monkeypatch.setattr(responses, "std_mock", std_mock_mock)
7981104 requests_mock.start()
7991105 assert len(patch_mock.call_args_list) == 1
800 assert patch_mock.call_args[1]['target'] == 'something.else'
1106 assert patch_mock.call_args[1]["target"] == "something.else"
1107
1108
1109 def _quote(s):
1110 return responses.quote(responses._ensure_str(s))
1111
1112
1113 def test_cookies_from_headers():
1114 text = "こんにちは/世界"
1115 quoted_text = _quote(text)
1116 expected = {"x": "a", "y": quoted_text}
1117 headers = {"set-cookie": "; ".join(k + "=" + v for k, v in expected.items())}
1118 cookiejar = responses._cookies_from_headers(headers)
1119 for k, v in cookiejar.items():
1120 assert isinstance(v, str)
1121 assert v == expected[k]
1122
1123
1124 @pytest.mark.parametrize(
1125 "url",
1126 (
1127 "http://example.com",
1128 "http://example.com/some/path",
1129 "http://example.com/other/path/",
1130 ),
1131 )
1132 def test_request_param(url):
1133 @responses.activate
1134 def run():
1135 params = {"hello": "world", "example": "params"}
1136 responses.add(
1137 method=responses.GET,
1138 url="{0}?hello=world".format(url),
1139 body="test",
1140 match_querystring=False,
1141 )
1142 resp = requests.get(url, params=params)
1143 assert_response(resp, "test")
1144 assert resp.request.params == params
1145
1146 resp = requests.get(url)
1147 assert_response(resp, "test")
1148 assert resp.request.params == {}
1149
1150 run()
1151 assert_reset()
1152
1153
1154 def test_request_param_with_multiple_values_for_the_same_key():
1155 @responses.activate
1156 def run():
1157 url = "http://example.com"
1158 params = {"key1": ["one", "two"], "key2": "three"}
1159 responses.add(
1160 method=responses.GET,
1161 url=url,
1162 body="test",
1163 )
1164 resp = requests.get(url, params=params)
1165 assert_response(resp, "test")
1166 assert resp.request.params == params
1167
1168 run()
1169 assert_reset()
1170
1171
1172 @pytest.mark.parametrize(
1173 "url", ("http://example.com", "http://example.com?hello=world")
1174 )
1175 def test_assert_call_count(url):
1176 @responses.activate
1177 def run():
1178 responses.add(responses.GET, url)
1179 responses.add(responses.GET, "http://example1.com")
1180
1181 assert responses.assert_call_count(url, 0) is True
1182
1183 with pytest.raises(AssertionError) as excinfo:
1184 responses.assert_call_count(url, 2)
1185 assert "Expected URL '{0}' to be called 2 times. Called 0 times.".format(
1186 url
1187 ) in str(excinfo.value)
1188
1189 requests.get(url)
1190 assert responses.assert_call_count(url, 1) is True
1191
1192 requests.get("http://example1.com")
1193 assert responses.assert_call_count(url, 1) is True
1194
1195 requests.get(url)
1196 with pytest.raises(AssertionError) as excinfo:
1197 responses.assert_call_count(url, 3)
1198 assert "Expected URL '{0}' to be called 3 times. Called 2 times.".format(
1199 url
1200 ) in str(excinfo.value)
1201
1202 run()
1203 assert_reset()
1204
1205
1206 def test_request_matches_post_params():
1207 @responses.activate
1208 def run():
1209 responses.add(
1210 method=responses.POST,
1211 url="http://example.com/",
1212 body="one",
1213 match=[
1214 responses.json_params_matcher(
1215 {"page": {"name": "first", "type": "json"}}
1216 )
1217 ],
1218 )
1219 responses.add(
1220 method=responses.POST,
1221 url="http://example.com/",
1222 body="two",
1223 match=[
1224 responses.urlencoded_params_matcher(
1225 {"page": "second", "type": "urlencoded"}
1226 )
1227 ],
1228 )
1229
1230 resp = requests.request(
1231 "POST",
1232 "http://example.com/",
1233 headers={"Content-Type": "x-www-form-urlencoded"},
1234 data={"page": "second", "type": "urlencoded"},
1235 )
1236 assert_response(resp, "two")
1237
1238 resp = requests.request(
1239 "POST",
1240 "http://example.com/",
1241 headers={"Content-Type": "application/json"},
1242 json={"page": {"name": "first", "type": "json"}},
1243 )
1244 assert_response(resp, "one")
1245
1246 run()
1247 assert_reset()
1248
1249
1250 def test_request_matches_empty_body():
1251 @responses.activate
1252 def run():
1253 responses.add(
1254 method=responses.POST,
1255 url="http://example.com/",
1256 body="one",
1257 match=[responses.json_params_matcher(None)],
1258 )
1259
1260 responses.add(
1261 method=responses.POST,
1262 url="http://example.com/",
1263 body="two",
1264 match=[responses.urlencoded_params_matcher(None)],
1265 )
1266
1267 resp = requests.request("POST", "http://example.com/")
1268 assert_response(resp, "one")
1269
1270 resp = requests.request(
1271 "POST",
1272 "http://example.com/",
1273 headers={"Content-Type": "x-www-form-urlencoded"},
1274 )
1275 assert_response(resp, "two")
1276
1277 run()
1278 assert_reset()
1279
1280
1281 def test_fail_request_error():
1282 @responses.activate
1283 def run():
1284 responses.add("POST", "http://example1.com")
1285 responses.add("GET", "http://example.com")
1286 responses.add(
1287 "POST",
1288 "http://example.com",
1289 match=[responses.urlencoded_params_matcher({"foo": "bar"})],
1290 )
1291
1292 with pytest.raises(ConnectionError) as excinfo:
1293 requests.post("http://example.com", data={"id": "bad"})
1294 msg = str(excinfo.value)
1295 assert "- POST http://example1.com/ URL does not match" in msg
1296 assert "- GET http://example.com/ Method does not match" in msg
1297 assert "- POST http://example.com/ Parameters do not match" in msg
1298
1299 run()
1300 assert_reset()
0 [tox]
1 envlist = py27,py35,py36,py37,py38,py39
2
3 [testenv]
4 extras = tests
5 commands =
6 pytest . --cov responses --cov-report term-missing