README.rst 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. Responses
  2. =========
  3. .. image:: https://img.shields.io/pypi/v/responses.svg
  4. :target: https://pypi.python.org/pypi/responses/
  5. .. image:: https://img.shields.io/pypi/pyversions/responses.svg
  6. :target: https://pypi.org/project/responses/
  7. .. image:: https://codecov.io/gh/getsentry/responses/branch/master/graph/badge.svg
  8. :target: https://codecov.io/gh/getsentry/responses/
  9. A utility library for mocking out the ``requests`` Python library.
  10. .. note::
  11. Responses requires Python 2.7 or newer, and requests >= 2.0
  12. Installing
  13. ----------
  14. ``pip install responses``
  15. Basics
  16. ------
  17. The core of ``responses`` comes from registering mock responses:
  18. .. code-block:: python
  19. import responses
  20. import requests
  21. @responses.activate
  22. def test_simple():
  23. responses.add(responses.GET, 'http://twitter.com/api/1/foobar',
  24. json={'error': 'not found'}, status=404)
  25. resp = requests.get('http://twitter.com/api/1/foobar')
  26. assert resp.json() == {"error": "not found"}
  27. assert len(responses.calls) == 1
  28. assert responses.calls[0].request.url == 'http://twitter.com/api/1/foobar'
  29. assert responses.calls[0].response.text == '{"error": "not found"}'
  30. If you attempt to fetch a url which doesn't hit a match, ``responses`` will raise
  31. a ``ConnectionError``:
  32. .. code-block:: python
  33. import responses
  34. import requests
  35. from requests.exceptions import ConnectionError
  36. @responses.activate
  37. def test_simple():
  38. with pytest.raises(ConnectionError):
  39. requests.get('http://twitter.com/api/1/foobar')
  40. Lastly, you can pass an ``Exception`` as the body to trigger an error on the request:
  41. .. code-block:: python
  42. import responses
  43. import requests
  44. @responses.activate
  45. def test_simple():
  46. responses.add(responses.GET, 'http://twitter.com/api/1/foobar',
  47. body=Exception('...'))
  48. with pytest.raises(Exception):
  49. requests.get('http://twitter.com/api/1/foobar')
  50. Response Parameters
  51. -------------------
  52. Responses are automatically registered via params on ``add``, but can also be
  53. passed directly:
  54. .. code-block:: python
  55. import responses
  56. responses.add(
  57. responses.Response(
  58. method='GET',
  59. url='http://example.com',
  60. )
  61. )
  62. The following attributes can be passed to a Response mock:
  63. method (``str``)
  64. The HTTP method (GET, POST, etc).
  65. url (``str`` or compiled regular expression)
  66. The full resource URL.
  67. match_querystring (``bool``)
  68. DEPRECATED: Use `responses.matchers.query_param_matcher` or
  69. `responses.matchers.query_string_matcher`
  70. Include the query string when matching requests.
  71. Enabled by default if the response URL contains a query string,
  72. disabled if it doesn't or the URL is a regular expression.
  73. body (``str`` or ``BufferedReader``)
  74. The response body.
  75. json
  76. A Python object representing the JSON response body. Automatically configures
  77. the appropriate Content-Type.
  78. status (``int``)
  79. The HTTP status code.
  80. content_type (``content_type``)
  81. Defaults to ``text/plain``.
  82. headers (``dict``)
  83. Response headers.
  84. stream (``bool``)
  85. DEPRECATED: use ``stream`` argument in request directly
  86. auto_calculate_content_length (``bool``)
  87. Disabled by default. Automatically calculates the length of a supplied string or JSON body.
  88. match (``list``)
  89. A list of callbacks to match requests based on request attributes.
  90. Current module provides multiple matchers that you can use to match:
  91. * body contents in JSON format
  92. * body contents in URL encoded data format
  93. * request query parameters
  94. * request query string (similar to query parameters but takes string as input)
  95. * kwargs provided to request e.g. ``stream``, ``verify``
  96. * 'multipart/form-data' content and headers in request
  97. * request headers
  98. * request fragment identifier
  99. Alternatively user can create custom matcher.
  100. Read more `Matching Requests`_
  101. Matching Requests
  102. -----------------
  103. When adding responses for endpoints that are sent request data you can add
  104. matchers to ensure your code is sending the right parameters and provide
  105. different responses based on the request body contents. Responses provides
  106. matchers for JSON and URL-encoded request bodies and you can supply your own for
  107. other formats.
  108. .. code-block:: python
  109. import responses
  110. import requests
  111. from responses import matchers
  112. @responses.activate
  113. def test_calc_api():
  114. responses.add(
  115. responses.POST,
  116. url='http://calc.com/sum',
  117. body="4",
  118. match=[
  119. matchers.urlencoded_params_matcher({"left": "1", "right": "3"})
  120. ]
  121. )
  122. requests.post("http://calc.com/sum", data={"left": 1, "right": 3})
  123. Matching JSON encoded data can be done with ``matchers.json_params_matcher()``.
  124. If your application uses other encodings you can build your own matcher that
  125. returns ``True`` or ``False`` if the request parameters match. Your matcher can
  126. expect a ``request`` parameter to be provided by responses.
  127. Similarly, you can use the ``matchers.query_param_matcher`` function to match
  128. against the ``params`` request parameter.
  129. Note, you must set ``match_querystring=False``
  130. .. code-block:: python
  131. import responses
  132. import requests
  133. from responses import matchers
  134. @responses.activate
  135. def test_calc_api():
  136. url = "http://example.com/test"
  137. params = {"hello": "world", "I am": "a big test"}
  138. responses.add(
  139. method=responses.GET,
  140. url=url,
  141. body="test",
  142. match=[matchers.query_param_matcher(params)],
  143. match_querystring=False,
  144. )
  145. resp = requests.get(url, params=params)
  146. constructed_url = r"http://example.com/test?I+am=a+big+test&hello=world"
  147. assert resp.url == constructed_url
  148. assert resp.request.url == constructed_url
  149. assert resp.request.params == params
  150. As alternative, you can use query string value in ``matchers.query_string_matcher``
  151. .. code-block:: python
  152. import requests
  153. import responses
  154. from responses import matchers
  155. @responses.activate
  156. def my_func():
  157. responses.add(
  158. responses.GET,
  159. "https://httpbin.org/get",
  160. match=[matchers.query_string_matcher("didi=pro&test=1")],
  161. )
  162. resp = requests.get("https://httpbin.org/get", params={"test": 1, "didi": "pro"})
  163. my_func()
  164. To validate request arguments use the ``matchers.request_kwargs_matcher`` function to match
  165. against the request kwargs.
  166. Note, only arguments provided to ``matchers.request_kwargs_matcher`` will be validated
  167. .. code-block:: python
  168. import responses
  169. import requests
  170. from responses import matchers
  171. with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
  172. req_kwargs = {
  173. "stream": True,
  174. "verify": False,
  175. }
  176. rsps.add(
  177. "GET",
  178. "http://111.com",
  179. match=[matchers.request_kwargs_matcher(req_kwargs)],
  180. )
  181. requests.get("http://111.com", stream=True)
  182. # >>> Arguments don't match: {stream: True, verify: True} doesn't match {stream: True, verify: False}
  183. To validate request body and headers for ``multipart/form-data`` data you can use
  184. ``matchers.multipart_matcher``. The ``data``, and ``files`` parameters provided will be compared
  185. to the request:
  186. .. code-block:: python
  187. import requests
  188. import responses
  189. from responses.matchers import multipart_matcher
  190. @responses.activate
  191. def my_func():
  192. req_data = {"some": "other", "data": "fields"}
  193. req_files = {"file_name": b"Old World!"}
  194. responses.add(
  195. responses.POST, url="http://httpbin.org/post",
  196. match=[multipart_matcher(req_files, data=req_data)]
  197. )
  198. resp = requests.post("http://httpbin.org/post", files={"file_name": b"New World!"})
  199. my_func()
  200. # >>> raises ConnectionError: multipart/form-data doesn't match. Request body differs.
  201. To validate request URL fragment identifier you can use ``matchers.fragment_identifier_matcher``.
  202. The matcher takes fragment string (everything after ``#`` sign) as input for comparison:
  203. .. code-block:: python
  204. import requests
  205. import responses
  206. from responses.matchers import fragment_identifier_matcher
  207. @responses.activate
  208. def run():
  209. url = "http://example.com?ab=xy&zed=qwe#test=1&foo=bar"
  210. responses.add(
  211. responses.GET,
  212. url,
  213. match_querystring=True,
  214. match=[fragment_identifier_matcher("test=1&foo=bar")],
  215. body=b"test",
  216. )
  217. # two requests to check reversed order of fragment identifier
  218. resp = requests.get("http://example.com?ab=xy&zed=qwe#test=1&foo=bar")
  219. resp = requests.get("http://example.com?zed=qwe&ab=xy#foo=bar&test=1")
  220. run()
  221. Matching Request Headers
  222. ------------------------
  223. When adding responses you can specify matchers to ensure that your code is
  224. sending the right headers and provide different responses based on the request
  225. headers.
  226. .. code-block:: python
  227. import responses
  228. import requests
  229. from responses import matchers
  230. @responses.activate
  231. def test_content_type():
  232. responses.add(
  233. responses.GET,
  234. url="http://example.com/",
  235. body="hello world",
  236. match=[
  237. matchers.header_matcher({"Accept": "text/plain"})
  238. ]
  239. )
  240. responses.add(
  241. responses.GET,
  242. url="http://example.com/",
  243. json={"content": "hello world"},
  244. match=[
  245. matchers.header_matcher({"Accept": "application/json"})
  246. ]
  247. )
  248. # request in reverse order to how they were added!
  249. resp = requests.get("http://example.com/", headers={"Accept": "application/json"})
  250. assert resp.json() == {"content": "hello world"}
  251. resp = requests.get("http://example.com/", headers={"Accept": "text/plain"})
  252. assert resp.text == "hello world"
  253. Because ``requests`` will send several standard headers in addition to what was
  254. specified by your code, request headers that are additional to the ones
  255. passed to the matcher are ignored by default. You can change this behaviour by
  256. passing ``strict_match=True`` to the matcher to ensure that only the headers
  257. that you're expecting are sent and no others. Note that you will probably have
  258. to use a ``PreparedRequest`` in your code to ensure that ``requests`` doesn't
  259. include any additional headers.
  260. .. code-block:: python
  261. import responses
  262. import requests
  263. from responses import matchers
  264. @responses.activate
  265. def test_content_type():
  266. responses.add(
  267. responses.GET,
  268. url="http://example.com/",
  269. body="hello world",
  270. match=[
  271. matchers.header_matcher({"Accept": "text/plain"}, strict_match=True)
  272. ]
  273. )
  274. # this will fail because requests adds its own headers
  275. with pytest.raises(ConnectionError):
  276. requests.get("http://example.com/", headers={"Accept": "text/plain"})
  277. # a prepared request where you overwrite the headers before sending will work
  278. session = requests.Session()
  279. prepped = session.prepare_request(
  280. requests.Request(
  281. method="GET",
  282. url="http://example.com/",
  283. )
  284. )
  285. prepped.headers = {"Accept": "text/plain"}
  286. resp = session.send(prepped)
  287. assert resp.text == "hello world"
  288. Response Registry
  289. ---------------------------
  290. By default, ``responses`` will search all registered``Response`` objects and
  291. return a match. If only one ``Response`` is registered, the registry is kept unchanged.
  292. However, if multiple matches are found for the same request, then first match is returned and
  293. removed from registry.
  294. Such behavior is suitable for most of use cases, but to handle special conditions, you can
  295. implement custom registry which must follow interface of ``registries.FirstMatchRegistry``.
  296. Redefining the ``find`` method will allow you to create custom search logic and return
  297. appropriate ``Response``
  298. Example that shows how to set custom registry
  299. .. code-block:: python
  300. import responses
  301. from responses import registries
  302. class CustomRegistry(registries.FirstMatchRegistry):
  303. pass
  304. """ Before tests: <responses.registries.FirstMatchRegistry object> """
  305. # using function decorator
  306. @responses.activate(registry=CustomRegistry)
  307. def run():
  308. """ Within test: <__main__.CustomRegistry object> """
  309. run()
  310. """ After test: <responses.registries.FirstMatchRegistry object> """
  311. # using context manager
  312. with responses.RequestsMock(registry=CustomRegistry) as rsps:
  313. """ In context manager: <__main__.CustomRegistry object> """
  314. """
  315. After exit from context manager: <responses.registries.FirstMatchRegistry object>
  316. """
  317. Dynamic Responses
  318. -----------------
  319. You can utilize callbacks to provide dynamic responses. The callback must return
  320. a tuple of (``status``, ``headers``, ``body``).
  321. .. code-block:: python
  322. import json
  323. import responses
  324. import requests
  325. @responses.activate
  326. def test_calc_api():
  327. def request_callback(request):
  328. payload = json.loads(request.body)
  329. resp_body = {'value': sum(payload['numbers'])}
  330. headers = {'request-id': '728d329e-0e86-11e4-a748-0c84dc037c13'}
  331. return (200, headers, json.dumps(resp_body))
  332. responses.add_callback(
  333. responses.POST, 'http://calc.com/sum',
  334. callback=request_callback,
  335. content_type='application/json',
  336. )
  337. resp = requests.post(
  338. 'http://calc.com/sum',
  339. json.dumps({'numbers': [1, 2, 3]}),
  340. headers={'content-type': 'application/json'},
  341. )
  342. assert resp.json() == {'value': 6}
  343. assert len(responses.calls) == 1
  344. assert responses.calls[0].request.url == 'http://calc.com/sum'
  345. assert responses.calls[0].response.text == '{"value": 6}'
  346. assert (
  347. responses.calls[0].response.headers['request-id'] ==
  348. '728d329e-0e86-11e4-a748-0c84dc037c13'
  349. )
  350. You can also pass a compiled regex to ``add_callback`` to match multiple urls:
  351. .. code-block:: python
  352. import re, json
  353. from functools import reduce
  354. import responses
  355. import requests
  356. operators = {
  357. 'sum': lambda x, y: x+y,
  358. 'prod': lambda x, y: x*y,
  359. 'pow': lambda x, y: x**y
  360. }
  361. @responses.activate
  362. def test_regex_url():
  363. def request_callback(request):
  364. payload = json.loads(request.body)
  365. operator_name = request.path_url[1:]
  366. operator = operators[operator_name]
  367. resp_body = {'value': reduce(operator, payload['numbers'])}
  368. headers = {'request-id': '728d329e-0e86-11e4-a748-0c84dc037c13'}
  369. return (200, headers, json.dumps(resp_body))
  370. responses.add_callback(
  371. responses.POST,
  372. re.compile('http://calc.com/(sum|prod|pow|unsupported)'),
  373. callback=request_callback,
  374. content_type='application/json',
  375. )
  376. resp = requests.post(
  377. 'http://calc.com/prod',
  378. json.dumps({'numbers': [2, 3, 4]}),
  379. headers={'content-type': 'application/json'},
  380. )
  381. assert resp.json() == {'value': 24}
  382. test_regex_url()
  383. If you want to pass extra keyword arguments to the callback function, for example when reusing
  384. a callback function to give a slightly different result, you can use ``functools.partial``:
  385. .. code-block:: python
  386. from functools import partial
  387. ...
  388. def request_callback(request, id=None):
  389. payload = json.loads(request.body)
  390. resp_body = {'value': sum(payload['numbers'])}
  391. headers = {'request-id': id}
  392. return (200, headers, json.dumps(resp_body))
  393. responses.add_callback(
  394. responses.POST, 'http://calc.com/sum',
  395. callback=partial(request_callback, id='728d329e-0e86-11e4-a748-0c84dc037c13'),
  396. content_type='application/json',
  397. )
  398. You can see params passed in the original ``request`` in ``responses.calls[].request.params``:
  399. .. code-block:: python
  400. import responses
  401. import requests
  402. @responses.activate
  403. def test_request_params():
  404. responses.add(
  405. method=responses.GET,
  406. url="http://example.com?hello=world",
  407. body="test",
  408. match_querystring=False,
  409. )
  410. resp = requests.get('http://example.com', params={"hello": "world"})
  411. assert responses.calls[0].request.params == {"hello": "world"}
  412. Responses as a context manager
  413. ------------------------------
  414. .. code-block:: python
  415. import responses
  416. import requests
  417. def test_my_api():
  418. with responses.RequestsMock() as rsps:
  419. rsps.add(responses.GET, 'http://twitter.com/api/1/foobar',
  420. body='{}', status=200,
  421. content_type='application/json')
  422. resp = requests.get('http://twitter.com/api/1/foobar')
  423. assert resp.status_code == 200
  424. # outside the context manager requests will hit the remote server
  425. resp = requests.get('http://twitter.com/api/1/foobar')
  426. resp.status_code == 404
  427. Responses as a pytest fixture
  428. -----------------------------
  429. .. code-block:: python
  430. @pytest.fixture
  431. def mocked_responses():
  432. with responses.RequestsMock() as rsps:
  433. yield rsps
  434. def test_api(mocked_responses):
  435. mocked_responses.add(
  436. responses.GET, 'http://twitter.com/api/1/foobar',
  437. body='{}', status=200,
  438. content_type='application/json')
  439. resp = requests.get('http://twitter.com/api/1/foobar')
  440. assert resp.status_code == 200
  441. Responses inside a unittest setUp()
  442. -----------------------------------
  443. When run with unittest tests, this can be used to set up some
  444. generic class-level responses, that may be complemented by each test
  445. .. code-block:: python
  446. class TestMyApi(unittest.TestCase):
  447. def setUp(self):
  448. responses.add(responses.GET, 'https://example.com', body="within setup")
  449. # here go other self.responses.add(...)
  450. @responses.activate
  451. def test_my_func(self):
  452. responses.add(
  453. responses.GET,
  454. "https://httpbin.org/get",
  455. match=[matchers.query_param_matcher({"test": "1", "didi": "pro"})],
  456. body="within test"
  457. )
  458. resp = requests.get("https://example.com")
  459. resp2 = requests.get("https://httpbin.org/get", params={"test": "1", "didi": "pro"})
  460. print(resp.text)
  461. # >>> within setup
  462. print(resp2.text)
  463. # >>> within test
  464. Assertions on declared responses
  465. --------------------------------
  466. When used as a context manager, Responses will, by default, raise an assertion
  467. error if a url was registered but not accessed. This can be disabled by passing
  468. the ``assert_all_requests_are_fired`` value:
  469. .. code-block:: python
  470. import responses
  471. import requests
  472. def test_my_api():
  473. with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
  474. rsps.add(responses.GET, 'http://twitter.com/api/1/foobar',
  475. body='{}', status=200,
  476. content_type='application/json')
  477. assert_call_count
  478. -----------------
  479. Assert that the request was called exactly n times.
  480. .. code-block:: python
  481. import responses
  482. import requests
  483. @responses.activate
  484. def test_assert_call_count():
  485. responses.add(responses.GET, "http://example.com")
  486. requests.get("http://example.com")
  487. assert responses.assert_call_count("http://example.com", 1) is True
  488. requests.get("http://example.com")
  489. with pytest.raises(AssertionError) as excinfo:
  490. responses.assert_call_count("http://example.com", 1)
  491. assert "Expected URL 'http://example.com' to be called 1 times. Called 2 times." in str(excinfo.value)
  492. Multiple Responses
  493. ------------------
  494. You can also add multiple responses for the same url:
  495. .. code-block:: python
  496. import responses
  497. import requests
  498. @responses.activate
  499. def test_my_api():
  500. responses.add(responses.GET, 'http://twitter.com/api/1/foobar', status=500)
  501. responses.add(responses.GET, 'http://twitter.com/api/1/foobar',
  502. body='{}', status=200,
  503. content_type='application/json')
  504. resp = requests.get('http://twitter.com/api/1/foobar')
  505. assert resp.status_code == 500
  506. resp = requests.get('http://twitter.com/api/1/foobar')
  507. assert resp.status_code == 200
  508. Using a callback to modify the response
  509. ---------------------------------------
  510. If you use customized processing in `requests` via subclassing/mixins, or if you
  511. have library tools that interact with `requests` at a low level, you may need
  512. to add extended processing to the mocked Response object to fully simulate the
  513. environment for your tests. A `response_callback` can be used, which will be
  514. wrapped by the library before being returned to the caller. The callback
  515. accepts a `response` as it's single argument, and is expected to return a
  516. single `response` object.
  517. .. code-block:: python
  518. import responses
  519. import requests
  520. def response_callback(resp):
  521. resp.callback_processed = True
  522. return resp
  523. with responses.RequestsMock(response_callback=response_callback) as m:
  524. m.add(responses.GET, 'http://example.com', body=b'test')
  525. resp = requests.get('http://example.com')
  526. assert resp.text == "test"
  527. assert hasattr(resp, 'callback_processed')
  528. assert resp.callback_processed is True
  529. Passing through real requests
  530. -----------------------------
  531. In some cases you may wish to allow for certain requests to pass through responses
  532. and hit a real server. This can be done with the ``add_passthru`` methods:
  533. .. code-block:: python
  534. import responses
  535. @responses.activate
  536. def test_my_api():
  537. responses.add_passthru('https://percy.io')
  538. This will allow any requests matching that prefix, that is otherwise not
  539. registered as a mock response, to passthru using the standard behavior.
  540. Pass through endpoints can be configured with regex patterns if you
  541. need to allow an entire domain or path subtree to send requests:
  542. .. code-block:: python
  543. responses.add_passthru(re.compile('https://percy.io/\\w+'))
  544. Lastly, you can use the `response.passthrough` attribute on `BaseResponse` or
  545. use ``PassthroughResponse`` to enable a response to behave as a pass through.
  546. .. code-block:: python
  547. # Enable passthrough for a single response
  548. response = Response(responses.GET, 'http://example.com', body='not used')
  549. response.passthrough = True
  550. responses.add(response)
  551. # Use PassthroughResponse
  552. response = PassthroughResponse(responses.GET, 'http://example.com')
  553. responses.add(response)
  554. Viewing/Modifying registered responses
  555. --------------------------------------
  556. Registered responses are available as a public method of the RequestMock
  557. instance. It is sometimes useful for debugging purposes to view the stack of
  558. registered responses which can be accessed via ``responses.registered()``.
  559. The ``replace`` function allows a previously registered ``response`` to be
  560. changed. The method signature is identical to ``add``. ``response`` s are
  561. identified using ``method`` and ``url``. Only the first matched ``response`` is
  562. replaced.
  563. .. code-block:: python
  564. import responses
  565. import requests
  566. @responses.activate
  567. def test_replace():
  568. responses.add(responses.GET, 'http://example.org', json={'data': 1})
  569. responses.replace(responses.GET, 'http://example.org', json={'data': 2})
  570. resp = requests.get('http://example.org')
  571. assert resp.json() == {'data': 2}
  572. The ``upsert`` function allows a previously registered ``response`` to be
  573. changed like ``replace``. If the response is registered, the ``upsert`` function
  574. will registered it like ``add``.
  575. ``remove`` takes a ``method`` and ``url`` argument and will remove **all**
  576. matched responses from the registered list.
  577. Finally, ``reset`` will reset all registered responses.
  578. Contributing
  579. ------------
  580. Responses uses several linting and autoformatting utilities, so it's important that when
  581. submitting patches you use the appropriate toolchain:
  582. Clone the repository:
  583. .. code-block:: shell
  584. git clone https://github.com/getsentry/responses.git
  585. Create an environment (e.g. with ``virtualenv``):
  586. .. code-block:: shell
  587. virtualenv .env && source .env/bin/activate
  588. Configure development requirements:
  589. .. code-block:: shell
  590. make develop
  591. Responses uses `Pytest <https://docs.pytest.org/en/latest/>`_ for
  592. testing. You can run all tests by:
  593. .. code-block:: shell
  594. pytest
  595. And run a single test by:
  596. .. code-block:: shell
  597. pytest -k '<test_function_name>'
  598. To verify ``type`` compliance, run `mypy <https://github.com/python/mypy>`_ linter:
  599. .. code-block:: shell
  600. mypy --config-file=./mypy.ini -p responses
  601. To check code style and reformat it run:
  602. .. code-block:: shell
  603. pre-commit run --all-files
  604. Note: on some OS, you have to use ``pre_commit``