Skip to main content

Mock services.

Project description

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 ;)

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.tar.gz (8.6 kB view hashes)

Uploaded Source

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