for all kinds of socket animals, web-clients included - with gevent/asyncio/SSL support
...and then MicroPython's urequests (mocket >= 3.9.1)
In a nutshell, Mocket is monkey-patching on steroids for the socket and ssl modules.
It’s designed to serve two main purposes:
- As a low-level framework — for example, if you're building a client for a new database or protocol.
- As a ready-to-use mock — perfect for testing HTTP or HTTPS calls from any client library.
To demonstrate that Mocket is more than just a web client mocking tool, it even includes a simple Redis mock.
The main goal of Mocket is to make it easier to test Python clients that communicate using the socket protocol.
Mocket packages are available for openSUSE, NixOS, ALT Linux, NetBSD, and of course from PyPI.
Starting from 3.7.0, Mocket major version will follow the same numbering pattern as Python's and therefore indicate the most recent Python version that is supported.
FYI: the last version compatible with Python 2.7 is 3.9.4, bugfixing or backporting of features introduced after that release will only be available as commercial support.
Star the project on GitHub, Buy Me a Coffee clicking the button below or, even better, contribute with patches or documentation.

Read the following blog posts if you want to have a big picture of what Mocket is capable of:
- https://medium.com/p/mocket-is-alive-and-is-fighting-with-us-b2810d52597a
- https://hackernoon.com/make-development-great-again-faab769d264e
- https://hackernoon.com/httpretty-now-supports-asyncio-e310814704c6
- https://medium.com/@mindflayer/how-to-make-your-tests-fail-when-they-try-to-access-the-network-python-eb80090a6d24
- https://medium.com/@mindflayer/testing-in-an-asyncio-world-a9a0ad41b0c5
The starting point to understand how to use Mocket to write a custom mock is the following example:
As next step, you are invited to have a look at the implementation of both the mocks it provides:
- HTTP mock (similar to HTTPretty) - https://github.com/mindflayer/python-mocket/blob/main/mocket/mocks/mockhttp.py
- Redis mock (basic implementation) - https://github.com/mindflayer/python-mocket/blob/main/mocket/mocks/mockredis.py
Please also have a look at the huge test suite:
- Tests module at https://github.com/mindflayer/python-mocket/tree/main/tests
Using pip:
$ pip install mocket
Mocket uses xxhash when available instead of hashlib.md5 for creating hashes, you can install it as follows:
$ pip install mocket[speedups]
When opening an Issue, please add few lines of code as failing test, or -better- open its relative Pull request adding this test to our test suite.
Let's create a new virtualenv with all we need:
$ python3 -m venv example $ source example/bin/activate $ pip install pytest requests mocket
As second step, we create an example.py file as the following one:
importjsonfrommocketimportmocketizefrommocket.mocks.mockhttpimportEntryimportrequestsimportpytest@pytest.fixturedefresponse():
return {
"integer": 1,
"string": "asd",
"boolean": False,
}
@mocketize# use its decoratordeftest_json(response):
url_to_mock='https://testme.org/json'Entry.single_register(
Entry.GET,
url_to_mock,
body=json.dumps(response),
headers={'content-type': 'application/json'}
)
mocked_response=requests.get(url_to_mock).json()
assertresponse==mocked_response# OR use its context managerfrommocketimportMocketizerdeftest_json_with_context_manager(response):
url_to_mock='https://testme.org/json'Entry.single_register(
Entry.GET,
url_to_mock,
body=json.dumps(response),
headers={'content-type': 'application/json'}
)
withMocketizer():
mocked_response=requests.get(url_to_mock).json()
assertresponse==mocked_responseLet's fire our example test:
$ py.test example.py
NEW!!! Sometimes you just want your tests to fail when they attempt to use the network.
withMocketizer(strict_mode=True):
withpytest.raises(StrictMocketException):
requests.get("https://duckduckgo.com/")
# OR@mocketize(strict_mode=True)deftest_get():
withpytest.raises(StrictMocketException):
requests.get("https://duckduckgo.com/")You can specify exceptions as a list of hosts or host-port pairs.
withMocketizer(strict_mode=True, strict_mode_allowed=["localhost", ("intake.ourmetrics.net", 443)]):
...
# OR@mocketize(strict_mode=True, strict_mode_allowed=["localhost", ("intake.ourmetrics.net", 443)])deftest_get():
...Add this instruction at the end of the test execution:
Mocket.assert_fail_if_entries_not_served()It's very important that we test non-happy paths.
@mocketizedeftest_raise_exception(self):
url="http://github.com/fluidicon.png"Entry.single_register(Entry.GET, url, exception=socket.error())
withself.assertRaises(requests.exceptions.ConnectionError):
requests.get(url)importjsonfrommocketimportmocketizefrommocket.mocks.mockhttpimportEntryimportrequests@mocketizedeftest_can_handle():
Entry.single_register(
Entry.GET,
url,
body=json.dumps({"message": "Nope... not this time!"}),
headers={"content-type": "application/json"},
can_handle_fun=lambdapath, qs_dict: path=="/ip"andqs_dict,
)
Entry.single_register(
Entry.GET,
url,
body=json.dumps({"message": "There you go!"}),
headers={"content-type": "application/json"},
can_handle_fun=lambdapath, qs_dict: path=="/ip"andnotqs_dict,
)
resp=requests.get("https://httpbin.org/ip")
assertresp.status_code==200assertresp.json() == {"message": "There you go!"}You probably know what VCRpy is capable of, that's the mocket's way of achieving it:
@mocketize(truesocket_recording_dir=tempfile.mkdtemp())deftest_truesendall_with_recording_https():
url='https://httpbin.org/ip'requests.get(url, headers={"Accept": "application/json"})
resp=requests.get(url, headers={"Accept": "application/json"})
assertresp.status_code==200dump_filename=os.path.join(
Mocket.get_truesocket_recording_dir(),
Mocket.get_namespace() +'.json',
)
withio.open(dump_filename) asf:
response=json.load(f)
assertlen(response['httpbin.org']['443'].keys()) ==1Mocket HTTP mock can work as HTTPretty replacement for many different use cases. Two main features are missing, or better said, are implemented differently:
- URL entries containing regular expressions, Mocket implements can_handle_fun which is way simpler to use and more powerful;
- response body from functions (used mostly to fake errors, Mocket accepts an exception instead).
Both features are documented above.
Two features which are against the Zen of Python, at least imho (mindflayer), but of course I am open to call it into question.
Example:
importjsonimportaiohttpimportasynciofromunittestimportTestCasefrommocket.plugins.httprettyimporthttpretty, httprettifiedclassAioHttpEntryTestCase(TestCase):
@httprettifieddeftest_https_session(self):
url='https://httpbin.org/ip'httpretty.register_uri(
httpretty.GET,
url,
body=json.dumps(dict(origin='127.0.0.1')),
)
asyncdefmain(l):
asyncwithaiohttp.ClientSession(
loop=l, timeout=aiohttp.ClientTimeout(total=3)
) assession:
asyncwithsession.get(url) asget_response:
assertget_response.status==200assertawaitget_response.text() =='{"origin": "127.0.0.1"}'loop=asyncio.new_event_loop()
loop.set_debug(True)
loop.run_until_complete(main(loop))Using Mocket with asyncio based clients:
$ pip install aiohttp
Example:
# `aiohttp` creates SSLContext instances at import-time# that's why Mocket would get stuck when dealing with HTTPS# Importing the module while Mocket is in control (inside a# decorated test function or using its context manager would# be enough for making it work), the alternative is using a# custom TCPConnector which always returns a FakeSSLContext# from Mocket like this example is showing.importaiohttpimportpytestfrommocketimportasync_mocketizefrommocket.mocks.mockhttpimportEntryfrommocket.plugins.aiohttp_connectorimportMocketTCPConnector@pytest.mark.asyncio@async_mocketizeasyncdeftest_aiohttp():
""" The alternative to using the custom `connector` would be importing `aiohttp` when Mocket is already in control (inside the decorated test). """url="https://bar.foo/"data= {"message": "Hello"}
Entry.single_register(
Entry.GET,
url,
body=json.dumps(data),
headers={"content-type": "application/json"},
)
asyncwithaiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=3), connector=MocketTCPConnector()
) assession, session.get(url) asresponse:
response=awaitresponse.json()
assertresponse==dataUsing Mocket as pook engine:
$ pip install mocket[pook]
Example:
importpookfrommocket.plugins.pook_mock_engineimportMocketEnginepook.set_mock_engine(MocketEngine)
pook.on()
url='http://twitter.com/api/1/foobar'status=404response_json= {'error': 'foo'}
mock=pook.get(
url,
headers={'content-type': 'application/json'},
reply=status,
response_json=response_json,
)
mock.persist()
requests.get(url)
assertmock.calls==1resp=requests.get(url)
assertresp.status_code==statusassertresp.json() ==response_jsonassertmock.calls==2EuroPython 2013, Florence
