From 864e41141657576860b1e500ba2ad566588e8b5e Mon Sep 17 00:00:00 2001 From: jiangxinyu Date: Wed, 16 Dec 2020 20:16:23 +0800 Subject: [PATCH] init httmock project --- httmock-1.3.0.tar.gz | Bin 0 -> 4428 bytes python-httmock.spec | 57 +++++++ tests.py | 370 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 httmock-1.3.0.tar.gz create mode 100644 python-httmock.spec create mode 100644 tests.py diff --git a/httmock-1.3.0.tar.gz b/httmock-1.3.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..0c3e61534ab5205144949f179cfcae7a0e981a95 GIT binary patch literal 4428 zcmV-S5wq?eiwFqdI!{~z|72-%bT4RhbZu{AYb`M@GcGVLbYXG;?LGT*@-@@%>XBgyk{MGZ3xf8kH~HVYiwEC&_|E4h4KWvs z1Q*N2K7a*w!Q2V42qyP?9jEKG_jkTZX+t?aJXF6&>bHH+Q@^V0bh^ER?os!!+iUN% zJ8;$8A%{CJP~u2XPRLG31z$$1;Q(wl+t5kui241Ml)Ie#zaO5SUyRPbg8Ywqy;bt> zblQhT{=LKA0m#31)a&e!b}j#}UQUB>$=&&#AO~P};fj7M(ZlLMzsYy{ZVk9M1XLKMp&CJVYGejrF3F~H$Q zWafH|u#XcK3gY@?5-dW`rT&DGM_1g*_h>8*;`oQg5!?z2;1n8!a5u{W5-KFggn(X$ z{r&yN$47@sVx54`_dNv`8Aaa%f@OpEzQ>}7aQ3g*<h~zzLmm z2G2x*PCXQx3S1^%Mb^B&H@0xs84oJMr1hJC4W5`4M#0R z^5gL8&E@+m^5e<7cPAHD!}Afjd`C_%FV2Qn!^;b}dreL*{z1MQUYxZE175*<*vF7# z4}eS;`N*b{X(PsRB+LRuQWUa@J98({HGdw{IV1DnfpH&tL_)?FZiMoTfWRrD@!W+g zsE}2qemT}oU4E@4KmPx4axr{;KDu&T|K-O2cDs8}82=Bu$L)Ije~IGy6EB`J^6vcP z?1yt_F(rCvtd06NX#a1&``g~|;`QZ=`hQ2g?qR|I?;Y3v-^-LAn4nWC=$>}r`lRD@ ztP8qeeWG>4$~*!ku}1M?LHV*zP6$3mm%(#yIaEpa0(~eJiO8|u1Pitof^#8Jg%S8; zbMRK;TL*l-eZ}y7^OBQT+y%T}@<3Jn9$Qe?>l4-JTOI_9y>7dUh%D`f^hxc@*1qf) z`?6;&ns7H1QsIqq5?uzWB=^ZhwJF>fTey+8Zltjh8JL2bNCR!(J%q-(QLS zeFV(Ulbqb4#!o;+&|+MHdWSgBc=!~?2ric7BOvKzLjn|BMQX}KZJ+LW^{rkT(%A`o%=nmMKR}`_5K{~Y0 znXnsrSW->oke;D8#9g7xK(6Cd^oiWaG+>e65CsLe^-x5d zQ7@&J43*Mr)=(I`Cb9tsIzSyCU9g%34N)(pnj|gXu_%O*g5AJa{S%uAje<=-TWhkt z`5ev3w$G^t*QAa$&sXWl?=%-aHDI(5aRl&F*672e*>0sk=Axmo0YKyOj@(hV@$&{H zDiV@=ccmJkYcLCf@h#;U9V#hPhTc_*wqBjI8iW*P7nt{MMojFT2En}x^1WjeCIb*j zS@?h)VL=waZn)8P#mV=WjNm}QJ_&M zoqQ&(GVlKH=$GasNcxk}o;|S)QO6qE6G7wK|0xRmMup0u)}@lcR-hXL(2YN3RPT3Y zWWl#Az+Hg32SH&LAuv2JHP1_*j7`}z+fwVl>5tuOcjYXC=vt{BTm(Ka@G$M-S5bQ! zx=zL`#+f+Ue}pNIfe*9J7?68*Qz0|`uNr@)=6*mHHm-Bl@f}+}27DUz6UtgR^P(dt z=l8eZgMizz=#%#s-(6h(cwwD-U^w7vG4#`UFz0l!K;8Y`I9~m}adP15;EC|v9Q+V@ z2!bOg@ziDLNdw3kFvSbNsf%*3S3wBwA3Psv|J494P(h&nO7$z(=n~LStR zF2R3=mxlOe7)yS0>;GgI=q1u*FJY{R+X_up-!UC9wSp04F2sm+a|p zkY2S%UzT9bv)aa^TI;E=_t33PuE|!OxK-tO7d>t%zF&c7{Uj-tJ?Q6<=Mz2S?L6YA zd%~aZ0e^{FZSr)j^`7ktj~2DB4&ywN|EKR9HlOdg{w&ykHvjKw`CmuJ$H#U4*DpZ+ z&k_)Pa{TY0-D&q~|G$p^)$zYN{#VEU>iAzB|EuGFb^Nc6|JCuoI{sJ3|LXW(9shfw z_5bMd{kzlikt05eub2Pd?R1Ol|D&Ti|Nljb$@b5j{IHTJux0ciTv`zfnjv1S+#|Bq zX;1l~F|)~X3|`@VR1Kw9u`8a6ARK#W>R+ay_5SZF`>ztx^Pm4|cMA63ZTAjq`+td2 z$4+YdUt|B-;rFY}|1jSd?7!1Js`Gzdq!`!#&x4NBcG~rDP}~2rJ6Id-zf-dR!-LxY zf6@HE+7fE}PwhWkKIc2u+kfY{(>^Zd{~p!n|6iuaz2sT!Pec%Sk=Y)8I%{mOxqx`tXmfYSYzB;Cj)Ye^;>pM zu#dvD1MX*gm3b2TE{0k#cIOBGqVUEE?w^u3YKPob5iyMtBLri3pDV^=J7V5UMj4LS$jJh7YX@J}D8FYFRUt)!38kLQ0nmL5#Ns6$cLQ?Kn*ZlW}*gzMk5Iv3{>2JGYY1lZflB)Ex?~JJBe}F7;9<~&y7Sg!;Lhg zf@bVY<3$*mMoowLh}LezVz&2xI@GWNf1*W|$n{kvLhRDG1X_B8zfrtk(+ZeW$I^kt zIi(RB0_Xh5748E&gZ|qFFyQ3V&*|$WSr``>sL=@ZVQebLXrE)FQ<>ML?Um%E*zh97 ztCRp9m=V>%KU3xjYU_ttEK=sF^bNo7XhenJwytiG1`KVDoK8m$RHZHPE)4+YB$k0_ zsS%`~U<^*1TAo6Uj& zttfdsl5W%0W)8-K0;UZ5SysJ8nlx!vKsid(a-}A4wrl(lM!Tc*u)DWna9FZ z!jZQ`?E>UJlUWEY-HP1CF!eH!@B_p2vO5G50Za)@rHv`xAvSogM;EtKN?uVuhk5hW ztNTa1>dUaDl|AMGjr2H{4ei=E>1$zsE9ZMvlMJ0MkjL!;`ts0;jY?42*GoK_fF$cB zn%uDk)8n2_fl)W$hu!H*6*t#6Ks8@xCjrElnUl?hl8kiJb#q(vs5a}7BZNYYS6d^N zdbb|41W~Bv<<_`$Vn`9Ws&?Wq1XXP!yUp0Eo@|9aYo1S1_o9I-`^{7&MyHMX4rulm zSH9l_-8WsM`2drGoUn8)Ojrm64$~(qlVlXj6r+rTB%#<8Dy2h|d0>Vxc=SQ3F3jF} zS|%Q&eibp93A8{@Mr!(rgY_T=oUZLpIqp^wyq4${gjW+UaisKB4@kN7hZc=)Qav8e zT$vzO-OUfWN7v+!1JYRqqB?gC1IHDQ*$Swnm)8yrF??vtQKoa6d^O?LS=@h zn%MQ4OeZ1b)?~Z8yYlyB0*h@}I#T5;e-vaveLBa~=K!68;L)Tn=bDYKPOjdM#;2EO z=Rh^AcdS(_y;r8IrS+b!PkS9@kuXf-)mVy6$C$!72FFP*WWjTvW>-QZRqtT^x^^LF zFekHd$(>G76hN$WDV`T+u0wf&tQmH!ocl4J(R(I4V`~bN^s<#6hgp4Ur@@iA&$xkL zms?tEe3djQu(Y16n6bW5zAO9-X@n{M`K=pJ9MwapQi+WK$T2l5JHnWvr~_dMU;+iV z$Zja_8)kvXNK#fbw`u#pxI0^B+Nz4J+D;f3nU;9FRXgb1M_5v1n(CSY&#_388mEjI z7=&RE+Ks|fz=(rsnFz&@w6VRHhDSf~m>+@9+p7cPzU898;l?~Sg=w6hNq3fgY^()EI4`Fz0;hpMz3v=)wGHK^g4h^Q93w;cW+yva6 zqnCGM&XSpmWCgL7ChU@#W!6rk<<6GyILWK?J6Hu_AgE;(3^zZ$1A=tgTn$>D=_}=A zRqY_>e3FkD$A*2J$bC$8^*}vcb(?cBtBGErXHV(DW=U8XPuRExX7_!yTaoM%sQn7K zh;YjyyB*jpIIW2joO6TZax(jUfR(PtX0IdT)uwwK6?ky(dSz3!Wr+J^db&(cxNO)v zOxlPZd&|JS~DUYe(+gI%zt=Yxh zZYQWtr1+o6|4>Q(li7Udw- z-xIFeR&CzDDf@rk?5~aA|LGic68rBRb=3d=6$^K@FAMq)3N-p&7e{7VH*tx3#>t7Vf@$orXz(HF z)wOzduh07qG9en^ls@x|ms#jZa#R)?q_MXxj{RnibZ-IP&4O<@^qU3U_RMd#&E_M& zruX_wDr0m0CBrjJr;}$T46ObT&p@lQt_*ybKr=d%(dd@Ww1f3@6uScSPe@@j>v^^= Sb*ak>mj3`BYcJjacmM!U8`S~; literal 0 HcmV?d00001 diff --git a/python-httmock.spec b/python-httmock.spec new file mode 100644 index 0000000..b71a3fb --- /dev/null +++ b/python-httmock.spec @@ -0,0 +1,57 @@ +%{?python_enable_dependency_generator} +# Created by pyp2rpm-3.3.0 +%global pypi_name httmock + +Name: python-%{pypi_name} +Version: 1.3.0 +Release: 1 +Summary: A mocking library for requests + +License: ASL 2.0 +URL: https://github.com/patrys/httmock +Source0: https://files.pythonhosted.org/packages/source/h/httmock/httmock-%{version}.tar.gz +Source1: https://raw.githubusercontent.com/patrys/httmock/%{version}/tests.py +BuildArch: noarch + +%description +A mocking library for requests for Python. +You can use it to mock third-party APIs and test libraries +that use requests internally. + +%package -n python3-%{pypi_name} +Summary: %{summary} +%{?python_provide:%python_provide python3-%{pypi_name}} +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3dist(requests) >= 1 + +%description -n python3-%{pypi_name} +A mocking library for requests for Python. +You can use it to mock third-party APIs and test libraries +that use requests internally. + +%prep +%autosetup -n %{pypi_name}-%{version} +# Remove bundled egg-info +rm -rf %{pypi_name}.egg-info +cp %{SOURCE1} . + +%build +%py3_build + +%install +%py3_install + +%check +%{__python3} setup.py test + +%files -n python3-%{pypi_name} +%license LICENSE +%doc README.md +%{python3_sitelib}/__pycache__/* +%{python3_sitelib}/%{pypi_name}.py +%{python3_sitelib}/%{pypi_name}-%{version}-py?.?.egg-info + +%changelog +* Thu Dec 17 2020 jiangxinyu - 1.3.0-1 +- Init httmock project diff --git a/tests.py b/tests.py new file mode 100644 index 0000000..63c9dd4 --- /dev/null +++ b/tests.py @@ -0,0 +1,370 @@ +# -*- coding: utf-8 -*- +import requests +import unittest + +from httmock import (all_requests, response, urlmatch, with_httmock, HTTMock, + remember_called, text_type, binary_type) + + +@urlmatch(scheme='swallow') +def unmatched_scheme(url, request): + raise AssertionError('This is outrageous') + + +@urlmatch(path=r'^never$') +def unmatched_path(url, request): + raise AssertionError('This is outrageous') + + +@urlmatch(method='post') +def unmatched_method(url, request): + raise AssertionError('This is outrageous') + + +@urlmatch(netloc=r'(.*\.)?google\.com$', path=r'^/$') +def google_mock(url, request): + return 'Hello from Google' + + +@urlmatch(netloc=r'(.*\.)?google\.com$', path=r'^/$') +@remember_called +def google_mock_count(url, request): + return 'Hello from Google' + + +@urlmatch(scheme='http', netloc=r'(.*\.)?facebook\.com$') +def facebook_mock(url, request): + return 'Hello from Facebook' + + +@urlmatch(scheme='http', netloc=r'(.*\.)?facebook\.com$') +@remember_called +def facebook_mock_count(url, request): + return 'Hello from Facebook' + +@urlmatch(netloc=r'(.*\.)?google\.com$', path=r'^/$', method='POST') +@remember_called +def google_mock_store_requests(url, request): + return 'Posting at Google' + + +@all_requests +def charset_utf8(url, request): + return { + 'content': u'Motörhead'.encode('utf-8'), + 'status_code': 200, + 'headers': { + 'Content-Type': 'text/plain; charset=utf-8' + } + } + + +def any_mock(url, request): + return 'Hello from %s' % (url.netloc,) + + +def dict_any_mock(url, request): + return { + 'content': 'Hello from %s' % (url.netloc,), + 'status_code': 200 + } + + +def example_400_response(url, res): + r = requests.Response() + r.status_code = 400 + r._content = b'Bad request.' + return r + + +class MockTest(unittest.TestCase): + + def test_return_type(self): + with HTTMock(any_mock): + r = requests.get('http://domain.com/') + self.assertTrue(isinstance(r, requests.Response)) + self.assertTrue(isinstance(r.content, binary_type)) + self.assertTrue(isinstance(r.text, text_type)) + + def test_scheme_fallback(self): + with HTTMock(unmatched_scheme, any_mock): + r = requests.get('http://example.com/') + self.assertEqual(r.content, b'Hello from example.com') + + def test_path_fallback(self): + with HTTMock(unmatched_path, any_mock): + r = requests.get('http://example.com/') + self.assertEqual(r.content, b'Hello from example.com') + + def test_method_fallback(self): + with HTTMock(unmatched_method, any_mock): + r = requests.get('http://example.com/') + self.assertEqual(r.content, b'Hello from example.com') + + def test_netloc_fallback(self): + with HTTMock(google_mock, facebook_mock): + r = requests.get('http://google.com/') + self.assertEqual(r.content, b'Hello from Google') + with HTTMock(google_mock, facebook_mock): + r = requests.get('http://facebook.com/') + self.assertEqual(r.content, b'Hello from Facebook') + + def test_400_response(self): + with HTTMock(example_400_response): + r = requests.get('http://example.com/') + self.assertEqual(r.status_code, 400) + self.assertEqual(r.content, b'Bad request.') + + def test_real_request_fallback(self): + with HTTMock(any_mock): + with HTTMock(google_mock, facebook_mock): + r = requests.get('http://example.com/') + self.assertEqual(r.status_code, 200) + self.assertEqual(r.content, b'Hello from example.com') + + def test_invalid_intercept_response_raises_value_error(self): + @all_requests + def response_content(url, request): + return -1 + with HTTMock(response_content): + self.assertRaises(TypeError, requests.get, 'http://example.com/') + + def test_encoding_from_contenttype(self): + with HTTMock(charset_utf8): + r = requests.get('http://example.com/') + self.assertEqual(r.encoding, 'utf-8') + self.assertEqual(r.text, u'Motörhead') + self.assertEqual(r.content, r.text.encode('utf-8')) + + +class DecoratorTest(unittest.TestCase): + + @with_httmock(any_mock) + def test_decorator(self): + r = requests.get('http://example.com/') + self.assertEqual(r.content, b'Hello from example.com') + + @with_httmock(any_mock) + def test_iter_lines(self): + r = requests.get('http://example.com/') + self.assertEqual(list(r.iter_lines()), + [b'Hello from example.com']) + + +class AllRequestsDecoratorTest(unittest.TestCase): + + def test_all_requests_response(self): + @all_requests + def response_content(url, request): + return {'status_code': 200, 'content': 'Oh hai'} + with HTTMock(response_content): + r = requests.get('https://example.com/') + self.assertEqual(r.status_code, 200) + self.assertEqual(r.content, b'Oh hai') + + def test_all_str_response(self): + @all_requests + def response_content(url, request): + return 'Hello' + with HTTMock(response_content): + r = requests.get('https://example.com/') + self.assertEqual(r.content, b'Hello') + + +class AllRequestsMethodDecoratorTest(unittest.TestCase): + @all_requests + def response_content(self, url, request): + return {'status_code': 200, 'content': 'Oh hai'} + + def test_all_requests_response(self): + with HTTMock(self.response_content): + r = requests.get('https://example.com/') + self.assertEqual(r.status_code, 200) + self.assertEqual(r.content, b'Oh hai') + + @all_requests + def string_response_content(self, url, request): + return 'Hello' + + def test_all_str_response(self): + with HTTMock(self.string_response_content): + r = requests.get('https://example.com/') + self.assertEqual(r.content, b'Hello') + + +class UrlMatchMethodDecoratorTest(unittest.TestCase): + @urlmatch(netloc=r'(.*\.)?google\.com$', path=r'^/$') + def google_mock(self, url, request): + return 'Hello from Google' + + @urlmatch(scheme='http', netloc=r'(.*\.)?facebook\.com$') + def facebook_mock(self, url, request): + return 'Hello from Facebook' + + @urlmatch(query=r'.*page=test') + def query_page_mock(self, url, request): + return 'Hello from test page' + + def test_netloc_fallback(self): + with HTTMock(self.google_mock, facebook_mock): + r = requests.get('http://google.com/') + self.assertEqual(r.content, b'Hello from Google') + with HTTMock(self.google_mock, facebook_mock): + r = requests.get('http://facebook.com/') + self.assertEqual(r.content, b'Hello from Facebook') + + def test_query(self): + with HTTMock(self.query_page_mock, self.google_mock): + r = requests.get('http://google.com/?page=test') + r2 = requests.get('http://google.com/') + self.assertEqual(r.content, b'Hello from test page') + self.assertEqual(r2.content, b'Hello from Google') + + +class ResponseTest(unittest.TestCase): + + content = {'name': 'foo', 'ipv4addr': '127.0.0.1'} + content_list = list(content.keys()) + + def test_response_auto_json(self): + r = response(0, self.content) + self.assertTrue(isinstance(r.content, binary_type)) + self.assertTrue(isinstance(r.text, text_type)) + self.assertEqual(r.json(), self.content) + r = response(0, self.content_list) + self.assertEqual(r.json(), self.content_list) + + def test_response_status_code(self): + r = response(200) + self.assertEqual(r.status_code, 200) + + def test_response_headers(self): + r = response(200, None, {'Content-Type': 'application/json'}) + self.assertEqual(r.headers['content-type'], 'application/json') + + def test_response_cookies(self): + @all_requests + def response_content(url, request): + return response(200, 'Foo', {'Set-Cookie': 'foo=bar;'}, + request=request) + with HTTMock(response_content): + r = requests.get('https://example.com/') + self.assertEqual(len(r.cookies), 1) + self.assertTrue('foo' in r.cookies) + self.assertEqual(r.cookies['foo'], 'bar') + + def test_response_session_cookies(self): + @all_requests + def response_content(url, request): + return response(200, 'Foo', {'Set-Cookie': 'foo=bar;'}, + request=request) + session = requests.Session() + with HTTMock(response_content): + r = session.get('https://foo_bar') + self.assertEqual(len(r.cookies), 1) + self.assertTrue('foo' in r.cookies) + self.assertEqual(r.cookies['foo'], 'bar') + self.assertEqual(len(session.cookies), 1) + self.assertTrue('foo' in session.cookies) + self.assertEqual(session.cookies['foo'], 'bar') + + def test_python_version_encoding_differences(self): + # Previous behavior would result in this test failing in Python3 due + # to how requests checks for utf-8 JSON content in requests.utils with: + # + # TypeError: Can't convert 'bytes' object to str implicitly + @all_requests + def get_mock(url, request): + return {'content': self.content, + 'headers': {'content-type': 'application/json'}, + 'status_code': 200, + 'elapsed': 5} + + with HTTMock(get_mock): + res = requests.get('http://example.com/') + self.assertEqual(self.content, res.json()) + + def test_mock_redirect(self): + @urlmatch(netloc='example.com') + def get_mock(url, request): + return {'status_code': 302, + 'headers': {'Location': 'http://google.com/'}} + + with HTTMock(get_mock, google_mock): + res = requests.get('http://example.com/') + self.assertEqual(len(res.history), 1) + self.assertEqual(res.content, b'Hello from Google') + + +class StreamTest(unittest.TestCase): + @with_httmock(any_mock) + def test_stream_request(self): + r = requests.get('http://domain.com/', stream=True) + self.assertEqual(r.raw.read(), b'Hello from domain.com') + + @with_httmock(dict_any_mock) + def test_stream_request_with_dict_mock(self): + r = requests.get('http://domain.com/', stream=True) + self.assertEqual(r.raw.read(), b'Hello from domain.com') + + @with_httmock(any_mock) + def test_non_stream_request(self): + r = requests.get('http://domain.com/') + self.assertEqual(r.raw.read(), b'') + + +class RememberCalledTest(unittest.TestCase): + + @staticmethod + def several_calls(count, method, *args, **kwargs): + results = [] + for _ in range(count): + results.append(method(*args, **kwargs)) + return results + + def test_several_calls(self): + with HTTMock(google_mock_count, facebook_mock_count): + results = self.several_calls( + 3, requests.get, 'http://facebook.com/') + + self.assertTrue(facebook_mock_count.call['called']) + self.assertEqual(facebook_mock_count.call['count'], 3) + + self.assertFalse(google_mock_count.call['called']) + self.assertEqual(google_mock_count.call['count'], 0) + + for r in results: + self.assertEqual(r.content, b'Hello from Facebook') + + # Negative case: cleanup call data + with HTTMock(facebook_mock_count): + results = self.several_calls( + 1, requests.get, 'http://facebook.com/') + + self.assertEquals(facebook_mock_count.call['count'], 1) + + @with_httmock(google_mock_count, facebook_mock_count) + def test_several_call_decorated(self): + results = self.several_calls(3, requests.get, 'http://facebook.com/') + + self.assertTrue(facebook_mock_count.call['called']) + self.assertEqual(facebook_mock_count.call['count'], 3) + + self.assertFalse(google_mock_count.call['called']) + self.assertEqual(google_mock_count.call['count'], 0) + + for r in results: + self.assertEqual(r.content, b'Hello from Facebook') + + self.several_calls(1, requests.get, 'http://facebook.com/') + self.assertEquals(facebook_mock_count.call['count'], 4) + + def test_store_several_requests(self): + with HTTMock(google_mock_store_requests): + payload = {"query": "foo"} + requests.post('http://google.com', data=payload) + + self.assertTrue(google_mock_store_requests.call['called']) + self.assertEqual(google_mock_store_requests.call['count'], 1) + request = google_mock_store_requests.call['requests'][0] + self.assertEqual(request.body, 'query=foo') -- Gitee