/python-mocket

a socket mock framework - for all kinds of socket animals, web-clients included

Primary LanguagePythonBSD 3-Clause "New" or "Revised" LicenseBSD-3-Clause

mocket /mɔˈkɛt/

https://api.travis-ci.org/mindflayer/python-mocket.svg?branch=master https://coveralls.io/repos/github/mindflayer/python-mocket/badge.svg?branch=master Code Climate Requirements Status

A socket mock framework

for all kinds of socket animals, web-clients included - with gevent/asyncio/SSL support

Support it

Star the project on GitHub, Buy Me a Coffee or, even better, contribute with patches or documentation.

Buy Me A Coffee

How to use it

Read these three blog posts if you want to have a big picture of what Mocket is capable of:

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 both the implementation of the two mocks it provides:

Please also have a look at the huge test suite:

Installation

Using pip:

$ pip install mocket

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.

Quick example of its HTTP mock

Let's create a new virtualenv with all we need:

$ virtualenv example
$ source example/bin/activate
$ pip install pytest requests mocket

As second step, we create an example.py file as the following one:

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 Mocketizer

def 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

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.

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:

import json

import aiohttp
import asyncio
import async_timeout
from unittest import TestCase

from 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) as session:
                with async_timeout.timeout(3):
                    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.get_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:

class AioHttpEntryTestCase(TestCase):
    @mocketize
    def test_http_session(self):
        url = 'http://httpbin.org/ip'
        body = "asd" * 100
        Entry.single_register(Entry.GET, url, body=body, status=404)
        Entry.single_register(Entry.POST, url, body=body*2, status=201)

        async def main(l):
            async with aiohttp.ClientSession(loop=l) as session:
                with async_timeout.timeout(3):
                    async with session.get(url) as get_response:
                        assert get_response.status == 404
                        assert await get_response.text() == body

                with async_timeout.timeout(3):
                    async with session.post(url, data=body * 6) as post_response:
                        assert post_response.status == 201
                        assert await post_response.text() == body * 2

        loop = asyncio.get_event_loop()
        loop.run_until_complete(main(loop))

Works well with others

Using Mocket as pook engine:

$ pip install mocket[pook]

Example:

import pook
from mocket.plugins.pook_mock_engine import MocketEngine

pook.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 == 1

resp = requests.get(url)
assert resp.status_code == status
assert resp.json() == response_json
assert mock.calls == 2

First appearance

EuroPython 2013, Florence