test_mocker.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  2. # not use this file except in compliance with the License. You may obtain
  3. # a copy of the License at
  4. #
  5. # https://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. # License for the specific language governing permissions and limitations
  11. # under the License.
  12. import json
  13. import pickle
  14. try:
  15. from unittest import mock
  16. except ImportError:
  17. import mock
  18. import requests
  19. import requests_mock
  20. from requests_mock import adapter
  21. from requests_mock import exceptions
  22. from requests_mock import response
  23. from . import base
  24. original_send = requests.Session.send
  25. class MockerTests(base.TestCase):
  26. def assertMockStarted(self):
  27. self.assertNotEqual(original_send, requests.Session.send)
  28. def assertMockStopped(self):
  29. self.assertEqual(original_send, requests.Session.send)
  30. def _do_test(self, m):
  31. self.assertMockStarted()
  32. matcher = m.register_uri('GET', 'http://www.test.com', text='resp')
  33. resp = requests.get('http://www.test.com')
  34. self.assertEqual('resp', resp.text)
  35. return matcher
  36. def test_multiple_starts(self):
  37. mocker = requests_mock.Mocker()
  38. self.assertMockStopped()
  39. mocker.start()
  40. self.assertMockStarted()
  41. self.assertRaises(RuntimeError, mocker.start)
  42. mocker.stop()
  43. self.assertMockStopped()
  44. mocker.stop()
  45. def test_with_session(self):
  46. url = 'http://test.url/path'
  47. url_inner = 'http://test.url/inner'
  48. url_outer = 'http://test.url/outer'
  49. with requests_mock.Mocker() as global_mock:
  50. global_mock.get(url_outer, text='global')
  51. session_a = requests.Session()
  52. session_b = requests.Session()
  53. session_a_original_send = session_a.send
  54. session_b_original_send = session_b.send
  55. self.assertNotEqual(session_a_original_send,
  56. session_b_original_send)
  57. mocker_a = requests_mock.Mocker(session=session_a)
  58. mocker_b = requests_mock.Mocker(session=session_b)
  59. mocker_a.start()
  60. mocker_b.start()
  61. mocker_a.register_uri('GET', url, text='resp_a')
  62. mocker_a.register_uri('GET', url_outer, real_http=True)
  63. mocker_b.register_uri('GET', url, text='resp_b')
  64. with requests_mock.Mocker(session=session_b) as mocker_b_inner:
  65. mocker_b_inner.register_uri('GET', url, real_http=True)
  66. mocker_b_inner.register_uri('GET',
  67. url_inner,
  68. text='resp_b_inner')
  69. self.assertEqual('resp_a', session_a.get(url).text)
  70. self.assertEqual('resp_b', session_b.get(url).text)
  71. self.assertRaises(exceptions.NoMockAddress,
  72. session_a.get,
  73. url_inner)
  74. self.assertEqual('resp_b_inner', session_b.get(url_inner).text)
  75. self.assertEqual('resp_a', session_a.get(url).text)
  76. self.assertEqual('resp_b', session_b.get(url).text)
  77. self.assertRaises(exceptions.NoMockAddress,
  78. session_a.get,
  79. url_inner)
  80. self.assertRaises(exceptions.NoMockAddress,
  81. session_b.get,
  82. url_inner)
  83. self.assertEqual('global', session_a.get(url_outer).text)
  84. self.assertRaises(exceptions.NoMockAddress,
  85. session_b.get,
  86. url_outer)
  87. self.assertNotEqual(session_a.send, session_a_original_send)
  88. self.assertNotEqual(session_b.send, session_b_original_send)
  89. self.assertNotEqual(session_a.send, session_b.send)
  90. mocker_a.stop()
  91. mocker_b.stop()
  92. self.assertEqual(session_a.send, session_a_original_send)
  93. self.assertEqual(session_b.send, session_b_original_send)
  94. self.assertEqual(requests.Session.send, original_send)
  95. def test_with_context_manager(self):
  96. self.assertMockStopped()
  97. with requests_mock.Mocker() as m:
  98. self._do_test(m)
  99. self.assertMockStopped()
  100. @mock.patch('requests.adapters.HTTPAdapter.send')
  101. @requests_mock.mock(real_http=True)
  102. def test_real_http(self, real_send, mocker):
  103. url = 'http://www.google.com/'
  104. test_text = 'real http data'
  105. test_bytes = test_text.encode('utf-8')
  106. # using create_response is a bit bootstrappy here but so long as it's
  107. # coming from HTTPAdapter.send it's ok
  108. req = requests.Request(method='GET', url=url)
  109. real_send.return_value = response.create_response(req.prepare(),
  110. status_code=200,
  111. content=test_bytes)
  112. resp = requests.get(url)
  113. self.assertEqual(1, real_send.call_count)
  114. self.assertEqual(url, real_send.call_args[0][0].url)
  115. self.assertEqual(test_text, resp.text)
  116. self.assertEqual(test_bytes, resp.content)
  117. @mock.patch('requests.adapters.HTTPAdapter.send')
  118. def test_real_http_changes(self, real_send):
  119. url = 'http://www.google.com/'
  120. test_text = 'real http data'
  121. test_bytes = test_text.encode('utf-8')
  122. req = requests.Request(method='GET', url=url)
  123. real_send.return_value = response.create_response(req.prepare(),
  124. status_code=200,
  125. content=test_bytes)
  126. with requests_mock.Mocker() as m:
  127. # real_http defaults to false so should raise NoMockAddress
  128. self.assertRaises(exceptions.NoMockAddress,
  129. requests.get,
  130. url)
  131. self.assertEqual(1, m.call_count)
  132. self.assertEqual(0, real_send.call_count)
  133. # change the value of real_http mid test
  134. m.real_http = True
  135. # fetch the url again and it should go through to the real url that
  136. # we've mocked out at a lower level.
  137. resp = requests.get(url)
  138. self.assertEqual(1, real_send.call_count)
  139. self.assertEqual(url, real_send.call_args[0][0].url)
  140. self.assertEqual(test_text, resp.text)
  141. self.assertEqual(test_bytes, resp.content)
  142. @mock.patch('requests.adapters.HTTPAdapter.send')
  143. def test_real_http_and_session(self, real_send):
  144. url = 'http://www.google.com/'
  145. test_text = 'real http data'
  146. test_bytes = test_text.encode('utf-8')
  147. req = requests.Request(method='GET', url=url)
  148. real_send.return_value = response.create_response(req.prepare(),
  149. status_code=200,
  150. content=test_bytes)
  151. session = requests.Session()
  152. with requests_mock.Mocker(session=session, real_http=True):
  153. resp = session.get(url)
  154. self.assertEqual(test_text, resp.text)
  155. self.assertEqual(test_bytes, resp.content)
  156. @requests_mock.mock()
  157. def test_with_test_decorator(self, m):
  158. self._do_test(m)
  159. @requests_mock.mock(kw='mock')
  160. def test_with_mocker_kwargs(self, **kwargs):
  161. self._do_test(kwargs['mock'])
  162. def test_with_decorator(self):
  163. @requests_mock.mock()
  164. def inner(m):
  165. self.assertMockStarted()
  166. self._do_test(m)
  167. self.assertMockStopped()
  168. inner()
  169. self.assertMockStopped()
  170. def test_with_decorator_called_multiple_times(self):
  171. @requests_mock.Mocker()
  172. def inner(arg1, m):
  173. self._do_test(m)
  174. self.assertEquals(
  175. len(m.request_history), 1,
  176. "Failed to provide clean mock on subsequent calls"
  177. )
  178. inner('a')
  179. # if we call the same decorated method again should get
  180. # a new request mock
  181. inner('b')
  182. def test_with_class_decorator(self):
  183. outer = self
  184. @requests_mock.mock()
  185. class Decorated(object):
  186. def test_will_be_decorated(self, m):
  187. outer.assertMockStarted()
  188. outer._do_test(m)
  189. def will_not_be_decorated(self):
  190. outer.assertMockStopped()
  191. decorated_class = Decorated()
  192. self.assertMockStopped()
  193. decorated_class.test_will_be_decorated()
  194. self.assertMockStopped()
  195. decorated_class.will_not_be_decorated()
  196. self.assertMockStopped()
  197. def test_with_class_decorator_and_custom_kw(self):
  198. outer = self
  199. @requests_mock.mock(kw='custom_m')
  200. class Decorated(object):
  201. def test_will_be_decorated(self, **kwargs):
  202. outer.assertMockStarted()
  203. outer._do_test(kwargs['custom_m'])
  204. def will_not_be_decorated(self):
  205. outer.assertMockStopped()
  206. decorated_class = Decorated()
  207. self.assertMockStopped()
  208. decorated_class.test_will_be_decorated()
  209. self.assertMockStopped()
  210. decorated_class.will_not_be_decorated()
  211. self.assertMockStopped()
  212. @mock.patch.object(requests_mock.mock, 'TEST_PREFIX', 'foo')
  213. def test_with_class_decorator_and_custom_test_prefix(self):
  214. outer = self
  215. @requests_mock.mock()
  216. class Decorated(object):
  217. def foo_will_be_decorated(self, m):
  218. outer.assertMockStarted()
  219. outer._do_test(m)
  220. def will_not_be_decorated(self):
  221. outer.assertMockStopped()
  222. decorated_class = Decorated()
  223. self.assertMockStopped()
  224. decorated_class.foo_will_be_decorated()
  225. self.assertMockStopped()
  226. decorated_class.will_not_be_decorated()
  227. self.assertMockStopped()
  228. @requests_mock.mock()
  229. def test_query_string(self, m):
  230. url = 'http://test.url/path'
  231. qs = 'a=1&b=2'
  232. m.register_uri('GET', url, text='resp')
  233. resp = requests.get("%s?%s" % (url, qs))
  234. self.assertEqual('resp', resp.text)
  235. self.assertEqual(qs, m.last_request.query)
  236. self.assertEqual(['1'], m.last_request.qs['a'])
  237. self.assertEqual(['2'], m.last_request.qs['b'])
  238. @requests_mock.mock()
  239. def test_mock_matcher_attributes(self, m):
  240. matcher = self._do_test(m)
  241. self.assertEqual(1, matcher.call_count)
  242. self.assertEqual(1, m.call_count)
  243. self.assertTrue(matcher.called)
  244. self.assertTrue(matcher.called_once)
  245. self.assertTrue(m.called)
  246. self.assertTrue(m.called_once)
  247. self.assertEqual(m.request_history, matcher.request_history)
  248. self.assertIs(m.last_request, matcher.last_request)
  249. def test_copy(self):
  250. mocker = requests_mock.mock(kw='foo', real_http=True)
  251. copy_of_mocker = mocker.copy()
  252. self.assertIsNot(copy_of_mocker, mocker)
  253. self.assertEqual(copy_of_mocker._kw, mocker._kw)
  254. self.assertEqual(copy_of_mocker.real_http, mocker.real_http)
  255. @requests_mock.mock()
  256. def test_reset_mock_reverts_call_count(self, request_mock):
  257. url = 'http://test.url/path'
  258. request_mock.get(url, text='resp')
  259. requests.get(url)
  260. self.assertEqual(request_mock.call_count, 1)
  261. # reset count and verify it is 0
  262. request_mock.reset_mock()
  263. self.assertEqual(request_mock.call_count, 0)
  264. class MockerHttpMethodsTests(base.TestCase):
  265. URL = 'http://test.com/path'
  266. TEXT = 'resp'
  267. def assertResponse(self, resp):
  268. self.assertEqual(self.TEXT, resp.text)
  269. @requests_mock.Mocker()
  270. def test_mocker_request(self, m):
  271. method = 'XXX'
  272. mock_obj = m.request(method, self.URL, text=self.TEXT)
  273. resp = requests.request(method, self.URL)
  274. self.assertResponse(resp)
  275. self.assertTrue(mock_obj.called)
  276. self.assertTrue(mock_obj.called_once)
  277. self.assertTrue(m.called)
  278. self.assertTrue(m.called_once)
  279. @requests_mock.Mocker()
  280. def test_mocker_get(self, m):
  281. mock_obj = m.get(self.URL, text=self.TEXT)
  282. self.assertResponse(requests.get(self.URL))
  283. self.assertTrue(mock_obj.called)
  284. self.assertTrue(mock_obj.called_once)
  285. self.assertTrue(m.called)
  286. self.assertTrue(m.called_once)
  287. @requests_mock.Mocker()
  288. def test_mocker_options(self, m):
  289. mock_obj = m.options(self.URL, text=self.TEXT)
  290. self.assertResponse(requests.options(self.URL))
  291. self.assertTrue(mock_obj.called)
  292. self.assertTrue(mock_obj.called_once)
  293. self.assertTrue(m.called)
  294. self.assertTrue(m.called_once)
  295. @requests_mock.Mocker()
  296. def test_mocker_head(self, m):
  297. mock_obj = m.head(self.URL, text=self.TEXT)
  298. self.assertResponse(requests.head(self.URL))
  299. self.assertTrue(mock_obj.called)
  300. self.assertTrue(mock_obj.called_once)
  301. self.assertTrue(m.called)
  302. self.assertTrue(m.called_once)
  303. @requests_mock.Mocker()
  304. def test_mocker_post(self, m):
  305. mock_obj = m.post(self.URL, text=self.TEXT)
  306. self.assertResponse(requests.post(self.URL))
  307. self.assertTrue(mock_obj.called)
  308. self.assertTrue(mock_obj.called_once)
  309. self.assertTrue(m.called)
  310. self.assertTrue(m.called_once)
  311. @requests_mock.Mocker()
  312. def test_mocker_put(self, m):
  313. mock_obj = m.put(self.URL, text=self.TEXT)
  314. self.assertResponse(requests.put(self.URL))
  315. self.assertTrue(mock_obj.called)
  316. self.assertTrue(mock_obj.called_once)
  317. self.assertTrue(m.called)
  318. self.assertTrue(m.called_once)
  319. @requests_mock.Mocker()
  320. def test_mocker_patch(self, m):
  321. mock_obj = m.patch(self.URL, text=self.TEXT)
  322. self.assertResponse(requests.patch(self.URL))
  323. self.assertTrue(mock_obj.called)
  324. self.assertTrue(mock_obj.called_once)
  325. self.assertTrue(m.called)
  326. self.assertTrue(m.called_once)
  327. @requests_mock.Mocker()
  328. def test_mocker_delete(self, m):
  329. mock_obj = m.delete(self.URL, text=self.TEXT)
  330. self.assertResponse(requests.delete(self.URL))
  331. self.assertTrue(mock_obj.called)
  332. self.assertTrue(mock_obj.called_once)
  333. self.assertTrue(m.called)
  334. self.assertTrue(m.called_once)
  335. @requests_mock.Mocker()
  336. def test_mocker_real_http_and_responses(self, m):
  337. self.assertRaises(RuntimeError,
  338. m.get,
  339. self.URL,
  340. text='abcd',
  341. real_http=True)
  342. @requests_mock.Mocker()
  343. def test_mocker_real_http(self, m):
  344. data = 'testdata'
  345. uri1 = 'fake://example.com/foo'
  346. uri2 = 'fake://example.com/bar'
  347. uri3 = 'fake://example.com/baz'
  348. m.get(uri1, text=data)
  349. m.get(uri2, real_http=True)
  350. self.assertEqual(data, requests.get(uri1).text)
  351. # This should fail because requests can't get an adapter for mock://
  352. # but it shows that it has tried and would have made a request.
  353. self.assertRaises(requests.exceptions.InvalidSchema,
  354. requests.get,
  355. uri2)
  356. # This fails because real_http is not set on the mocker
  357. self.assertRaises(exceptions.NoMockAddress,
  358. requests.get,
  359. uri3)
  360. # do it again to make sure the mock is still in place
  361. self.assertEqual(data, requests.get(uri1).text)
  362. @requests_mock.Mocker(case_sensitive=True)
  363. def test_case_sensitive_query(self, m):
  364. data = 'testdata'
  365. query = {'aBcDe': 'FgHiJ'}
  366. m.get(self.URL, text=data)
  367. resp = requests.get(self.URL, params=query)
  368. self.assertEqual('GET', m.last_request.method)
  369. self.assertEqual(200, resp.status_code)
  370. self.assertEqual(data, resp.text)
  371. for k, v in query.items():
  372. self.assertEqual([v], m.last_request.qs[k])
  373. @mock.patch.object(requests_mock.Mocker, 'case_sensitive', True)
  374. def test_global_case_sensitive(self):
  375. with requests_mock.mock() as m:
  376. data = 'testdata'
  377. query = {'aBcDe': 'FgHiJ'}
  378. m.get(self.URL, text=data)
  379. resp = requests.get(self.URL, params=query)
  380. self.assertEqual('GET', m.last_request.method)
  381. self.assertEqual(200, resp.status_code)
  382. self.assertEqual(data, resp.text)
  383. for k, v in query.items():
  384. self.assertEqual([v], m.last_request.qs[k])
  385. def test_nested_mocking(self):
  386. url1 = 'http://url1.com/path1'
  387. url2 = 'http://url2.com/path2'
  388. url3 = 'http://url3.com/path3'
  389. data1 = 'data1'
  390. data2 = 'data2'
  391. data3 = 'data3'
  392. with requests_mock.mock() as m1:
  393. r1 = m1.get(url1, text=data1)
  394. resp1a = requests.get(url1)
  395. self.assertRaises(exceptions.NoMockAddress, requests.get, url2)
  396. self.assertRaises(exceptions.NoMockAddress, requests.get, url3)
  397. self.assertEqual(data1, resp1a.text)
  398. # call count = 3 because there are 3 calls above, url 1-3
  399. self.assertEqual(3, m1.call_count)
  400. self.assertEqual(1, r1.call_count)
  401. with requests_mock.mock() as m2:
  402. r2 = m2.get(url2, text=data2)
  403. self.assertRaises(exceptions.NoMockAddress, requests.get, url1)
  404. resp2a = requests.get(url2)
  405. self.assertRaises(exceptions.NoMockAddress, requests.get, url3)
  406. self.assertEqual(data2, resp2a.text)
  407. with requests_mock.mock() as m3:
  408. r3 = m3.get(url3, text=data3)
  409. self.assertRaises(exceptions.NoMockAddress,
  410. requests.get,
  411. url1)
  412. self.assertRaises(exceptions.NoMockAddress,
  413. requests.get,
  414. url2)
  415. resp3 = requests.get(url3)
  416. self.assertEqual(data3, resp3.text)
  417. self.assertEqual(3, m3.call_count)
  418. self.assertEqual(1, r3.call_count)
  419. resp2b = requests.get(url2)
  420. self.assertRaises(exceptions.NoMockAddress, requests.get, url1)
  421. self.assertEqual(data2, resp2b.text)
  422. self.assertRaises(exceptions.NoMockAddress, requests.get, url3)
  423. self.assertEqual(3, m1.call_count)
  424. self.assertEqual(1, r1.call_count)
  425. self.assertEqual(6, m2.call_count)
  426. self.assertEqual(2, r2.call_count)
  427. self.assertEqual(3, m3.call_count)
  428. self.assertEqual(1, r3.call_count)
  429. resp1b = requests.get(url1)
  430. self.assertEqual(data1, resp1b.text)
  431. self.assertRaises(exceptions.NoMockAddress, requests.get, url2)
  432. self.assertRaises(exceptions.NoMockAddress, requests.get, url3)
  433. self.assertEqual(6, m1.call_count)
  434. self.assertEqual(2, r1.call_count)
  435. @requests_mock.mock()
  436. def test_mocker_additional(self, m):
  437. url = 'http://www.example.com'
  438. good_text = 'success'
  439. def additional_cb(req):
  440. return 'hello' in req.text
  441. m.post(url, additional_matcher=additional_cb, text=good_text)
  442. self.assertEqual(good_text,
  443. requests.post(url, data='hello world').text)
  444. self.assertRaises(exceptions.NoMockAddress,
  445. requests.post,
  446. url,
  447. data='goodbye world')
  448. @requests_mock.mock()
  449. def test_mocker_pickle(self, m):
  450. url = 'http://www.example.com'
  451. text = 'hello world'
  452. m.get(url, text=text)
  453. orig_resp = requests.get(url)
  454. self.assertEqual(text, orig_resp.text)
  455. d = pickle.dumps(orig_resp)
  456. new_resp = pickle.loads(d)
  457. self.assertEqual(text, new_resp.text)
  458. self.assertIsInstance(orig_resp.request.matcher, adapter._Matcher)
  459. self.assertIsNone(new_resp.request.matcher)
  460. @requests_mock.mock()
  461. def test_stream_zero_bytes(self, m):
  462. content = b'blah'
  463. m.get("http://test", content=content)
  464. res = requests.get("http://test", stream=True)
  465. zero_val = res.raw.read(0)
  466. self.assertEqual(b'', zero_val)
  467. self.assertFalse(res.raw.closed)
  468. full_val = res.raw.read()
  469. self.assertEqual(content, full_val)
  470. def test_with_json_encoder_on_mocker(self):
  471. test_val = 'hello world'
  472. class MyJsonEncoder(json.JSONEncoder):
  473. def encode(s, o):
  474. return test_val
  475. with requests_mock.Mocker(json_encoder=MyJsonEncoder) as m:
  476. m.get("http://test", json={"a": "b"})
  477. res = requests.get("http://test")
  478. self.assertEqual(test_val, res.text)
  479. @requests_mock.mock()
  480. def test_with_json_encoder_on_endpoint(self, m):
  481. test_val = 'hello world'
  482. class MyJsonEncoder(json.JSONEncoder):
  483. def encode(s, o):
  484. return test_val
  485. m.get("http://test", json={"a": "b"}, json_encoder=MyJsonEncoder)
  486. res = requests.get("http://test")
  487. self.assertEqual(test_val, res.text)
  488. @requests_mock.mock()
  489. def test_mismatch_content_length_streaming(self, m):
  490. url = "https://test/package.tar.gz"
  491. def f(request, context):
  492. context.headers["Content-Length"] = "300810"
  493. return None
  494. m.head(
  495. url=url,
  496. status_code=200,
  497. text=f,
  498. )
  499. requests.head(url)