pook

Versatile, expressive and hackable utility library for HTTP traffic mocking and expectations made easy in Python. Heavily inspired by gock.

Features

  • Simple, expressive and fluent API.
  • Provides both Pythonic and chainable DSL API styles.
  • Full-featured HTTP response definitions and expectations.
  • Matches any HTTP protocol primitive (URL, method, query params, headers, body...).
  • Full regular expressions capable mock expectations matching.
  • Supports most popular HTTP clients via interceptor adapters.
  • Configurable volatile, persistent or TTL limited mocks.
  • Works with any testing framework/engine (unittest, pytest, nosetests...).
  • First-class JSON & XML support matching and responses.
  • Supports JSON Schema body matching.
  • Works in both runtime and testing environments.
  • Can be used as decorator and/or via context managers.
  • Supports real networking mode with optional traffic filtering.
  • Map/filter mocks easily for generic or custom mock expectations.
  • Custom user-defined mock matcher functions.
  • Simulated raised error exceptions.
  • Network delay simulation (only available for aiohttp).
  • Pluggable and hackable API.
  • Customizable HTTP traffic mock interceptor engine.
  • Supports third-party mocking engines, such as mocket.
  • Fits good for painless test doubles.
  • Does not support WebSocket traffic mocking.
  • Works with Python +2.7 and +3.0 (including PyPy).
  • Dependency-less: just 2 small dependencies for JSONSchema and XML tree comparison.

Supported HTTP clients

pook can work with multiple mock engines, however it provides a built-in one by default, which currently supports traffic mocking in the following HTTP clients:

More HTTP clients can be supported progressively.

Note: only recent HTTP client package versions were tested.

Installation

Using pip package manager (requires pip 1.8+):

pip install --upgrade pook

Or install the latest sources from Github:

pip install -e git+git://github.com/h2non/pook.git#egg=pook

Getting started

See ReadTheDocs documentation:

Documentation Status

API

See annotated API reference documention.

Examples

See examples documentation for full featured code and use case examples.

Basic mocking:

import pook
import requests

@pook.on
def test_my_api():
    mock = pook.get('http://twitter.com/api/1/foobar', reply=404, response_json={'error': 'not found'})

    resp = requests.get('http://twitter.com/api/1/foobar')
    assert resp.status_code == 404
    assert resp.json() == {"error": "not found"}
    assert mock.calls == 1

Using the chainable API DSL:

import pook
import requests

@pook.on
def test_my_api():
    mock = (pook.get('http://twitter.com/api/1/foobar')
              .reply(404)
              .json({'error': 'not found'}))

    resp = requests.get('http://twitter.com/api/1/foobar')
    assert resp.json() == {"error": "not found"}
    assert mock.calls == 1

Using the decorator:

import pook
import requests

@pook.get('http://httpbin.org/status/500', reply=204)
@pook.get('http://httpbin.org/status/400', reply=200)
def fetch(url):
    return requests.get(url)

res = fetch('http://httpbin.org/status/400')
print('#1 status:', res.status_code)

res = fetch('http://httpbin.org/status/500')
print('#2 status:', res.status_code)

Simple unittest integration:

import pook
import unittest
import requests


class TestUnitTestEngine(unittest.TestCase):

    @pook.on
    def test_request(self):
        pook.get('server.com/foo').reply(204)
        res = requests.get('http://server.com/foo')
        self.assertEqual(res.status_code, 204)

    def test_request_with_context_manager(self):
        with pook.use():
            pook.get('server.com/bar', reply=204)
            res = requests.get('http://server.com/bar')
            self.assertEqual(res.status_code, 204)

Using the context manager for isolated HTTP traffic interception blocks:

import pook
import requests

# Enable HTTP traffic interceptor
with pook.use():
    pook.get('http://httpbin.org/status/500', reply=204)

    res = requests.get('http://httpbin.org/status/500')
    print('#1 status:', res.status_code)

# Interception-free HTTP traffic
res = requests.get('http://httpbin.org/status/200')
print('#2 status:', res.status_code)

Example using mocket Python library as underlying mock engine:

import pook
import requests
from mocket.plugins.pook_mock_engine import MocketEngine

# Use mocket library as underlying mock engine
pook.set_mock_engine(MocketEngine)

# Explicitly enable pook HTTP mocking (optional)
pook.on()

# Target server URL to mock out
url = 'http://twitter.com/api/1/foobar'

# Define your mock
mock = pook.get(url,
                reply=404, times=2,
                headers={'content-type': 'application/json'},
                response_json={'error': 'foo'})

# Run first HTTP request
requests.get(url)
assert mock.calls == 1

# Run second HTTP request
res = requests.get(url)
assert mock.calls == 2

# Assert response data
assert res.status_code == 404
assert res.json() == {'error': 'foo'}

# Explicitly disable pook (optional)
pook.off()

Example using Hy language (Lisp dialect for Python):

(import [pook])
(import [requests])

(defn request [url &optional [status 404]]
  (doto (.mock pook url) (.reply status))
  (let [res (.get requests url)]
    (. res status_code)))

(defn run []
  (with [(.use pook)]
    (print "Status:" (request "http://server.com/foo" :status 204))))

;; Run test program
(defmain [&args] (run))

Development

Clone the repository:

git clone [email protected]:h2non/pook.git

Install dependencies:

pip install -r requirements.txt -r requirements-dev.txt

Install Python dependencies:

make install

Lint code:

make lint

Run tests:

make test

Generate documentation:

make htmldocs

GitHub

https://github.com/h2non/pook