Package: httptest
httptestHTTP testing inspired by golang's httptest package. Supports wrapping asyncio
coroutine functions (async def).UsageContext Managerimportunittestimporturllib.requestimporthttptestclassTestHTTPServer(httptest.Handler):defdo_GET(self):contents="what up".encode()self.send_response(200)self.send_header("Content-type","text/plain")self.send_header("Content-length",len(contents))self.end_headers()self.wfile.write(contents)defmain():withhttptest.Server(TestHTTPServer)asts:withurllib.request.urlopen(ts.url())asf:assertf.read().decode('utf-8')=="what up"if__name__=='__main__':main()Simple HTTP Server Handlerimportunittestimporturllib.requestimporthttptestclassTestHTTPServer(httptest.Handler):defdo_GET(self):contents="what up".encode()self.send_response(200)self.send_header("Content-type","text/plain")self.send_header("Content-length",len(contents))self.end_headers()self.wfile.write(contents)classTestHTTPTestMethods(unittest.TestCase):@httptest.Server(TestHTTPServer)deftest_call_response(self,ts=httptest.NoServer()):withurllib.request.urlopen(ts.url())asf:self.assertEqual(f.read().decode('utf-8'),"what up")if__name__=='__main__':unittest.main()Serve Filesimportpathlibimportunittestimporthttp.serverimporturllib.requestimporthttptestFILE_PATH=pathlib.Path(__file__)classTestHTTPTestMethods(unittest.TestCase):@httptest.Server(lambda*args:http.server.SimpleHTTPRequestHandler(*args,directory=FILE_PATH.parent))deftest_call_response(self,ts=httptest.NoServer()):withurllib.request.urlopen(ts.url()+FILE_PATH.name)asf:self.assertEqual(f.read().decode('utf-8'),FILE_PATH.read_text())if__name__=='__main__':unittest.main()Asyncio SupportAsyncio support for the unittest package hasn't yet landed in Python.python/issue32972.
It should land in 3.8, check it outhere.If you want a quick way to addasynciotest cases you can import the helper
fromintel/dffml.importsysimportunittestimporturllib.requestifsys.version_info.minor==3\andsys.version_info.minor<=7:fromdffml.util.asynctestcaseimportAsyncTestCaseelse:# In Python 3.8fromunittestimportIsolatedAsyncioTestCaseasAsyncTestCaseimporthttptestclassTestHTTPServer(httptest.Handler):defdo_GET(self):contents="what up".encode()self.send_response(200)self.send_header("Content-type","text/plain")self.send_header("Content-length",len(contents))self.end_headers()self.wfile.write(contents)classTestHTTPTestMethods(AsyncTestCase):@httptest.Server(TestHTTPServer)asyncdeftest_call_response(self,ts=httptest.NoServer()):withurllib.request.urlopen(ts.url())asf:self.assertEqual(f.read().decode('utf-8'),"what up")if__name__=='__main__':unittest.main()In your project'ssetup.py, adddffmlintests_require.setup(name='your_package',...tests_require=['httptest>=0.1.0','dffml>=0.4.0.post0'])Auto InstallIf you're making a python package, you'll want to addhttptestto yoursetup.pyfile'stests_requiresection.This way, when your runpython setup.py testsetuptools will installhttptestfor you in a package local directory, if it's not already installed.setup(name='your_package',...tests_require=['httptest>=0.1.0'])OIDC ServerStart serverhttptest-oidc$python-mhttptest.oidc\--issuerhttps://this-service.example.com\--audiencehttps://relying-party.example.com\--addr0.0.0.0\--port8000\--subjecttest-subject\--private-key-pem-pathprivate-key.pem\--token-pathtoken.jwtMake requests$curl-H"Authorization: Bearer$(curlhttps://this-service.example.com/token|jq-rtoken)"-vhttps://relying-party.example.com$curl-H"Authorization: Bearer$(cattoken.jwt)"-vhttps://relying-party.example.comCache ServerRun the caceh server and use it's URL in place of the upstream URL whatever you want to intercept on$httptest-cache--state-dir.cache/httptest--addr0.0.0.0--port7000http://localhost:8000Serving on http://localhost:7000Inspect cached objects in the cache dir$python-c'import sys, pathlib, pickle, pprint; pprint.pprint(pickle.loads(pathlib.Path(sys.argv[-1]).read_bytes()).headers)'.cache/httptest/f31bc77712e808fffdab85a33631e414f25715588b1a026d6b8a4e0171b67e99859ab71b1933c93b0078d1e47da9a929.request.pickle{'Accept': '*/*','Accept-encoding': 'gzip','Connection': 'close','Content-length': '8159','Content-type': 'application/json','Host': 'localhost:45709','Request-hmac': '1700084205.d96d4f546acedddc142b1168642a74c738685d1ac4aa07984e9a1850bb73ddee','User-agent': 'GitHub-Hookshot/dc69923','X-as': '','X-country': '','X-forwarded-for': '10.56.101.48','X-forwarded-proto': 'https','X-github-delivery': '12dac8d6-83ff-11ee-97c9-119c09045ae0','X-github-event': 'push','X-github-hook-id': '443288828','X-github-hook-installation-target-id': '621131680','X-github-hook-installation-target-type': 'repository','X-github-request-id': '1271:336E:974AC:15C2D2:655539ED','X-glb-edge-region': 'iad','X-glb-edge-site': 'ash1-iad','X-glb-via': 'hostname=glb-proxy-1c66317.ash1-iad.github.net site=ash1-iad ''region=iad service=kube-public t=1700084205.902','X-haproxy-ssl-fc-cipher': 'TLS_AES_128_GCM_SHA256','X-haproxy-ssl-fc-protocol': 'TLSv1.3','X-haproxy-ssl-fc-use-keysize': '128','X-real-ip': '10.56.101.48','X-region': '','X-request-start': 't=1700084205915930','X-ssl-ja3-hash': '7a15285d4efc355608b304698cd7f9ab'}ExamplesSee theexamples/directory for more examples.github_webhook_event_logger.py