Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mindflayer/python-mocket
a socket mock framework - for all kinds of socket animals, web-clients included
https://github.com/mindflayer/python-mocket
aiohttp asyncio coverage framework http httpretty https micropython mock mocket mocking python socket socket-communication socket-programming ssl tdd tdd-utilities testing urequests
Last synced: about 6 hours ago
JSON representation
a socket mock framework - for all kinds of socket animals, web-clients included
- Host: GitHub
- URL: https://github.com/mindflayer/python-mocket
- Owner: mindflayer
- License: bsd-3-clause
- Created: 2013-04-30T07:44:28.000Z (over 11 years ago)
- Default Branch: main
- Last Pushed: 2024-11-26T11:20:15.000Z (17 days ago)
- Last Synced: 2024-12-06T01:07:13.452Z (7 days ago)
- Topics: aiohttp, asyncio, coverage, framework, http, httpretty, https, micropython, mock, mocket, mocking, python, socket, socket-communication, socket-programming, ssl, tdd, tdd-utilities, testing, urequests
- Language: Python
- Homepage:
- Size: 606 KB
- Stars: 284
- Watchers: 13
- Forks: 43
- Open Issues: 7
-
Metadata Files:
- Readme: README.rst
- License: LICENSE
Awesome Lists containing this project
- awesome-python-testing - mocket - A socket mock framework with gevent/asyncio/SSL support. (Mock and Stub)
- starred-awesome - python-mocket - a socket mock framework - for all kinds of socket animals, web-clients included (Python)
- best-of-web-python - GitHub - 3% open · ⏱️ 01.06.2024): (Web Testing)
README
===============
mocket /mɔˈkɛt/
===============.. image:: https://github.com/mindflayer/python-mocket/actions/workflows/main.yml/badge.svg?branch=main
:target: https://github.com/mindflayer/python-mocket/actions?query=workflow%3A%22Mocket%27s+CI%22.. image:: https://coveralls.io/repos/github/mindflayer/python-mocket/badge.svg?branch=main
:target: https://coveralls.io/github/mindflayer/python-mocket?branch=main.. image:: https://app.codacy.com/project/badge/Grade/6327640518ce42adaf59368217028f14
:target: https://www.codacy.com/gh/mindflayer/python-mocket/dashboard.. image:: https://img.shields.io/pypi/dm/mocket
:target: https://pypistats.org/packages/mocketA socket mock framework
-------------------------
for all kinds of socket *animals*, web-clients included - with gevent/asyncio/SSL support...and then MicroPython's *urequests* (*mocket >= 3.9.1*)
Outside GitHub
==============Mocket packages are available for `Arch Linux`_, `openSUSE`_, `NixOS`_, `ALT Linux`_, `NetBSD`_, and of course from `PyPI`_.
.. _`Arch Linux`: https://archlinux.org/packages/extra/any/python-mocket/
.. _`openSUSE`: https://software.opensuse.org/search?baseproject=ALL&q=mocket
.. _`NixOS`: https://search.nixos.org/packages?query=mocket
.. _`ALT Linux`: https://packages.altlinux.org/en/sisyphus/srpms/python3-module-mocket/
.. _`NetBSD`: https://cdn.netbsd.org/pub/pkgsrc/current/pkgsrc/devel/py-mocket/index.html
.. _`PyPI`: https://pypi.org/project/mocket/Versioning
==========
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.
Support it
==========
*Star* the project on GitHub, *Buy Me a Coffee* clicking the button below or, even better, contribute with patches or documentation... image:: https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png
:target: https://www.buymeacoffee.com/mocket
:alt: Buy Me A CoffeeHow to use it
=============
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-a9a0ad41b0c5The starting point to understand how to use *Mocket* to write a custom mock is the following example:
- https://github.com/mindflayer/mocketoy
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/master/mocket/mockhttp.py
- Redis mock (basic implementation) - https://github.com/mindflayer/python-mocket/blob/master/mocket/mockredis.pyPlease also have a look at the huge test suite:
- Tests module at https://github.com/mindflayer/python-mocket/tree/master/tests
Installation
============
Using pip::$ pip install mocket
Speedups
========
Mocket uses **xxhash** when available instead of *hashlib.md5* for creating hashes, you can install it as follows::$ pip install mocket[speedups]
Issues
============
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.Example of how to mock an HTTP[S] call
======================================
Let's create a new virtualenv with all we need::$ python3 -m venv example
$ source example/bin/activate
$ pip install pytest requests mocketAs second step, we create an `example.py` file as the following one:
.. code-block:: python
import json
from mocket import mocketize
from mocket.mockhttp import Entry
import requests
import pytest@pytest.fixture
def response():
return {
"integer": 1,
"string": "asd",
"boolean": False,
}@mocketize # use its decorator
def test_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()
assert response == mocked_response
# OR use its context manager
from mocket import Mocketizerdef test_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'}
)with Mocketizer():
mocked_response = requests.get(url_to_mock).json()assert response == mocked_response
Let's fire our example test::
$ py.test example.py
How to make Mocket fail when it tries to write to a real `socket`?
==================================================================
NEW!!! Sometimes you just want your tests to fail when they attempt to use the network... code-block:: python
with Mocketizer(strict_mode=True):
with pytest.raises(StrictMocketException):
requests.get("https://duckduckgo.com/")# OR
@mocketize(strict_mode=True)
def test_get():
with pytest.raises(StrictMocketException):
requests.get("https://duckduckgo.com/")You can specify exceptions as a list of hosts or host-port pairs.
.. code-block:: python
with Mocketizer(strict_mode=True, strict_mode_allowed=["localhost", ("intake.ourmetrics.net", 443)]):
...# OR
@mocketize(strict_mode=True, strict_mode_allowed=["localhost", ("intake.ourmetrics.net", 443)])
def test_get():
...How to be sure that all the Entry instances have been served?
=============================================================
Add this instruction at the end of the test execution:.. code-block:: python
Mocket.assert_fail_if_entries_not_served()
Example of how to fake socket errors
====================================It's very important that we test non-happy paths.
.. code-block:: python
@mocketize
def test_raise_exception(self):
url = "http://github.com/fluidicon.png"
Entry.single_register(Entry.GET, url, exception=socket.error())
with self.assertRaises(requests.exceptions.ConnectionError):
requests.get(url)Example of how to record real socket traffic
============================================You probably know what *VCRpy* is capable of, that's the *mocket*'s way of achieving it:
.. code-block:: python
@mocketize(truesocket_recording_dir=tempfile.mkdtemp())
def test_truesendall_with_recording_https():
url = 'https://httpbin.org/ip'requests.get(url, headers={"Accept": "application/json"})
resp = requests.get(url, headers={"Accept": "application/json"})
assert resp.status_code == 200dump_filename = os.path.join(
Mocket.get_truesocket_recording_dir(),
Mocket.get_namespace() + '.json',
)
with io.open(dump_filename) as f:
response = json.load(f)assert len(response['httpbin.org']['443'].keys()) == 1
HTTPretty compatibility layer
=============================
Mocket HTTP mock can work as *HTTPretty* replacement for many different use cases. Two main features are missing:- URL entries containing regular expressions;
- response body from functions (used mostly to fake errors, *mocket* doesn't need to do it this way).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:
.. code-block:: python
import json
import aiohttp
import asyncio
from unittest import TestCasefrom mocket.plugins.httpretty import httpretty, httprettified
class AioHttpEntryTestCase(TestCase):
@httprettified
def test_https_session(self):
url = 'https://httpbin.org/ip'
httpretty.register_uri(
httpretty.GET,
url,
body=json.dumps(dict(origin='127.0.0.1')),
)async def main(l):
async with aiohttp.ClientSession(
loop=l, timeout=aiohttp.ClientTimeout(total=3)
) as session:
async with session.get(url) as get_response:
assert get_response.status == 200
assert await get_response.text() == '{"origin": "127.0.0.1"}'loop = asyncio.new_event_loop()
loop.set_debug(True)
loop.run_until_complete(main(loop))What about the other socket animals?
====================================
Using *Mocket* with asyncio based clients::$ pip install aiohttp
Example:
.. code-block:: python
# `aiohttp` creates SSLContext instances at import-time
# that's why Mocket would get stuck when dealing with HTTP
# 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 return a FakeSSLContext
# from Mocket like this example is showing.
import aiohttp
import pytestfrom mocket import async_mocketize
from mocket.mockhttp import Entry
from mocket.plugins.aiohttp_connector import MocketTCPConnector@pytest.mark.asyncio
@async_mocketize
async def test_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"},
)async with aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=3), connector=MocketTCPConnector()
) as session, session.get(url) as response:
response = await response.json()
assert response == dataWorks well with others
=======================
Using *Mocket* as pook_ engine::$ pip install mocket[pook]
.. _pook: https://pypi.python.org/pypi/pook
Example:
.. code-block:: python
import pook
from mocket.plugins.pook_mock_engine import MocketEnginepook.set_mock_engine(MocketEngine)
pook.on()
url = 'http://twitter.com/api/1/foobar'
status = 404
response_json = {'error': 'foo'}mock = pook.get(
url,
headers={'content-type': 'application/json'},
reply=status,
response_json=response_json,
)
mock.persist()requests.get(url)
assert mock.calls == 1resp = requests.get(url)
assert resp.status_code == status
assert resp.json() == response_json
assert mock.calls == 2First appearance
================
EuroPython 2013, Florence- Video: https://www.youtube.com/watch?v=-LvXbl5d02U
- Slides: https://prezi.com/tmuiaugamsti/mocket/
- Slides as PDF: https://ep2013.europython.eu/media/conference/slides/mocket-a-socket-mock-framework.pdf