Skip to main content

Mock services.

Project description

=============
Mock services
=============

.. image:: https://circleci.com/gh/novafloss/mock-services.svg?style=shield
:target: https://circleci.com/gh/novafloss/mock-services
:alt: We are under CI!!

Aims to provide an easy way to mock an entire service API based on
`requests-mock`_ and a simple dict definition of a service. The idea is to mock
everything at start according given rules. Then `mock-services`_ allows to
*start/stop* http mock locally.

During our session we can:

- add rules
- permit external calls
- stop mocking
- reset rules
- restart mocking
- etc.


Mock endpoints explicitly
=========================


*Note:* rules urls must be regex. They always will be compiled before updating
the main `requests-mock`_ urls registry.

Let's mock our favorite search engine::

>>> def fake_duckduckgo_cb(request):
... return 200, {}, 'Coincoin!'

>>> rules = [
... {
... 'text': fake_duckduckgo_cb,
... 'headers': {'Content-Type': 'text/html'},
... 'method': 'GET',
... 'url': r'^https://duckduckgo.com/\?q='
... },
... ]

>>> from mock_services import update_http_rules
>>> update_http_rules(rules)

>>> import requests
>>> requests.get('https://duckduckgo.com/?q=mock-services').content[:15]
'<!DOCTYPE html>'

>>> from mock_services import start_http_mock
>>> start_http_mock()

>>> requests.get('https://duckduckgo.com/?q=mock-services').content
'Coincoin!'


When the http_mock is started if you try to call an external url, it should
fail::

>>> requests.get('https://www.google.com/#q=mock-services')
...
ConnectionError: Connection refused: GET https://www.google.com/#q=mock-services


Then you can allow external calls if needed::

>>> from mock_services import http_mock
>>> http_mock.set_allow_external(True)

>>> requests.get('https://www.google.com/#q=mock-services').content[:15]
'<!doctype html>'


At anytime you can stop the mocking as follow::

>>> from mock_services import stop_http_mock
>>> stop_http_mock()

>>> requests.get('https://duckduckgo.com/?q=mock-services').content[:15]
'<!DOCTYPE html>'


Or stop mocking during a function call::

>>> start_http_mock()

>>> @no_http_mock
... def please_do_not_mock_me():
... return requests.get('https://duckduckgo.com/?q=mock-services').content[:15] == '<!DOCTYPE html>', 'mocked!'

>>> please_do_not_mock_me


Or start mocking for another function call::

>>> stop_http_mock()

>>> @with_http_mock
... def please_mock_me():
... assert requests.get('https://duckduckgo.com/?q=mock-services').content == 'Coincoin', 'no mock!'

>>> please_mock_me


Mock service easy
=================


You can add REST rules with an explicit method. It will add rules as above and
automatically bind callbacks to fake a REST service.

*Note:* *resource* and *id* regex options are mandatory in the rules urls.

Additionally, `mock_services`_ include `attrs`_ library. It can be use for
field validation as follow.

This service mock will create, get, update and delete resources for you::

>>> import attr

>>> rest_rules = [
... {
... 'method': 'LIST',
... 'url': r'^http://my_fake_service/(?P<resource>api)$'
... },
... {
... 'method': 'GET',
... 'url': r'^http://my_fake_service/(?P<resource>api)/(?P<id>\d+)$',
... },
... {
... 'method': 'GET',
... 'url': r'^http://my_fake_service/(?P<resource>api)/(?P<id>\d+)/(?P<action>download)$',
... },
... {
... 'method': 'POST',
... 'url': r'^http://my_fake_service/(?P<resource>api)$',
... 'id_name': 'id',
... 'id_factory': int,
... 'attrs': {
... 'bar': attr.ib(),
... 'foo':attr.ib(default=True)
... }
... },
... {
... 'method': 'PATCH',
... 'url': r'^http://my_fake_service/(?P<resource>api)/(?P<id>\d+)$',
... },
... {
... 'method': 'DELETE',
... 'url': r'^http://my_fake_service/(?P<resource>api)/(?P<id>\d+)$'
... },
... ]

>>> from mock_services import update_rest_rules
>>> update_rest_rules(rest_rules)

>>> from mock_services import start_http_mock
>>> start_http_mock()

>>> response = requests.get('http://my_fake_service/api')
>>> response.status_code
200
>>> response.json()
[]

>>> response = requests.get('http://my_fake_service/api/1')
>>> response.status_code
404

>>> import json

>>> response = requests.post('http://my_fake_service/api',
... data=json.dumps({}),
... headers={'content-type': 'application/json'})
>>> response.status_code
400

>>> response = requests.post('http://my_fake_service/api',
... data=json.dumps({'bar': 'Python will save the world'}),
... headers={'content-type': 'application/json'})
>>> response.status_code
201
>>> response.json()
{
'id': 1,
'foo'; True,
'bar'; 'Python will save the world.'
}

>>> response = requests.patch('http://my_fake_service/api/1',
... data=json.dumps({'bar': "Python will save the world. I don't know how. But it will."}),
... headers={'content-type': 'application/json'})
>>> response.status_code
200

>>> response = requests.get('http://my_fake_service/api/1')
>>> response.status_code
200
>>> response.json()
{
'id': 1,
'foo'; True,
'bar'; "Python will save the world. I don't know how. But it will."
}

>>> response = requests.delete('http://my_fake_service/api/1')
>>> response.status_code
204


More validation
===============


Is some cases you need to validate a resource against another. Then you can add
global validators per endpoint as follow::

>>> from mock_services import storage
>>> from mock_services.service import ResourceContext
>>> from mock_services.exceptions import Http409

>>> def duplicate_foo(request):
... data = json.loads(request.body)
... ctx = ResourceContext(hostname='my_fake_service', resource='api')
... if data['foo'] in [o['foo'] for o in storage.list(ctx)]:
... raise Http409

>>> rest_rules_with_validators = [
... {
... 'method': 'POST',
... 'url': r'^http://my_fake_service/(?P<resource>api)$',
... 'validators': [
... duplicate_foo,
... ],
... },
... ]

>>> response = requests.post('http://my_fake_service/api',
... data=json.dumps({'foo': 'bar'}),
... headers={'content-type': 'application/json'})
>>> response.status_code
201

>>> response = requests.post('http://my_fake_service/api',
... data=json.dumps({'foo': 'bar'}),
... headers={'content-type': 'application/json'})
>>> response.status_code
409


Have fun in testing external APIs ;)


.. _`attrs`: https://github.com/hynek/attrs
.. _`requests-mock`: https://github.com/openstack/requests-mock
.. _`mock-services`: https://github.com/novafloss/mock-services

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mock-services-0.3.1.tar.gz (8.7 kB view details)

Uploaded Source

Built Distribution

mock_services-0.3.1-py2-none-any.whl (12.1 kB view details)

Uploaded Python 2

File details

Details for the file mock-services-0.3.1.tar.gz.

File metadata

File hashes

Hashes for mock-services-0.3.1.tar.gz
Algorithm Hash digest
SHA256 b07d51a55b18b7399a2c30fdb9008697df17d7717a04afccc9f89656ce82c193
MD5 92513ec7e175c011ef6deca8e8e894b6
BLAKE2b-256 a26d291552d2b8c710de5736d4db821707d48f03cecafe908ccfc3dbf08da183

See more details on using hashes here.

File details

Details for the file mock_services-0.3.1-py2-none-any.whl.

File metadata

File hashes

Hashes for mock_services-0.3.1-py2-none-any.whl
Algorithm Hash digest
SHA256 dc3a3c69b122f6647857f12748cb02a195e8216ba7b8513352d96c2a2768933a
MD5 e1b130bb22c4ec35af503703d892c034
BLAKE2b-256 1e7e294d8a5b2edf1880ba6f709b8461029a6971e72e646cb906bcf40784ea3a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page