Skip to content

everhide/restub

Repository files navigation

RESTUB is a library for creating HTTP stubs

TL;DR

For example, creation of the stub returning us Hello World:

import requests
from restub import Service

with Service(routes=['GET', r'/$', 'Hello world']) as srv:
    requests.get(srv.host)

Preamble

Often while developing there is a need to create some HTTP stub. Frequent solutions of this problem among developers are:

  • Using shareware and non - free software, like SoapUI
  • Creating service from scratch, for example, using a Flask
  • For unit - tests, using such packages as requests - mock

Each of these solutions has the right for life. I needed free, completely transparent and simple package creating a really existing server in system. A server that could be invoked both from code and applications, like browser or curl. I found a lot of similar libraries, but for various reasons they did not fit me. So the restub appeared.

Main functionality:

  • Automatic addition of necessary headers according to a contents type
  • Tracing of an output of requests and responses
  • Support of regular expressions in URL
  • Emulation of a slow connection
  • Support of HTTPS

Routes and routing

A route represents the ordered sequence of values(method, path, data, headers, status) describing data which we can receive at the specified address and a method of access. Therefore the method of access and the address is a required and other values can be omitted.

method — an access method, can be "GET", "POST", "PUT" or "DELETE"

path — describing the response address, can be regex

* data — response data, can be str or dict

* headers — HTTP response headers

* status — code of the response status

When data passed the headers Content - type and Content - length will be automatically added in response. Of course, you can always override these headers. Having sent the dict as data the header 'Content-type' with the value 'application/json' will be added. When str passed, the following scenarios are possible:

  • If the str is a path to the file existing in system, contents of this file will be load in a body of response. At the same time, if the extension of the file has a matching with one of CTYPES values(the dictionary containing often used formats of data, such as “css”, “js”, “ttf”, etc), the Content - type will be taken there
  • If the str represents json, xml or html document, then the Content - type will have the corresponding values: 'application/json', 'application/xml' or 'text/html'
  • In all other cases, data will be transferred as 'text/plain'

Running

The stub can be run as a context manager, a decorator of function or as a class instance. Before the run of a stub at least one route has to be defined. The address where the stub is started can be received through the property - host. By default the stub is available at the address http://localhost:8081 or https://localhost:8081 if the secure mode was enabled.

Run as the a context manager with a change of port:

from restub import Service

with Service(routes=['GET', r'/$'], port=7777) as srv:
    # your requests here

Run as the decorator of function:

from restub import Service

@Service(routes=['GET', r'/$'])
def stubbed_func():
    # your requests here

Run as the class instance:

from restub import Service

srv = Service(routes=['GET', r'/$'])
srv.start()
# your requests here
srv.stop()

Run as the class instance and definition of routes through the functions of the same name:

from restub import Service

srv = Service()
srv.get(r'/$')  # post(..), put(..), delete(..)
srv.start()
# your requests here
srv.stop()

For work with HTTPS it is necessary to set secure flag in True and pass absolute paths to a private key and a certificate:

from restub import Service

with Service(routes=['GET', r'/$'], secure=True, crt='<abs path to key>', key='<abs path to cert>'):
    # your secured requests here

The private key and the certificate in linux can be generated by the command:

openssl req -new -x509 -days 365 -nodes -out restub.crt -keyout restub.key

The slow connection can be emulated through delay property. It specifies the delay per response in seconds:

from restub import Service

with Service(routes=['GET', r'/$'], delay=0.5) as srv:
    # your delayed requests here

Tracing of an output of requests and responses turns on by the setting of trace flag in False:

from restub import Service

with Service(routes=['GET', r'/$'], trace=True) as srv:
    # your requests with trace here

Examples

Example with the sample web page and css file

At first, we need to create files: index.html and style.css.

Content of index.html:

<html>
<head>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="center">Restub test</div>
</body>
</html>

Content of style.css:

body {
    background-color:  #CCC;
}

.center {
    margin: auto;
    padding: 10px;
    width: 50% ;
    border: 3px solid  #000;
    background:  #FFF;
    text-align: center;
    text-transform: uppercase;
}

Our script:

from restub import Service

service = Service()
service.get(r'/$', '/home/user/../../index.html')
service.get(r'/style.css$', '/home/user/../../style.css')
try:
    service.start()
    input('Open the page on http://localhost:8081. Press enter for exit...')
except KeyboardInterrupt:
    pass
finally:
    service.stop()

We specify in routes absolute paths to the files above. Now, run this example, open your browser and go to http://localhost:8081.

Example with a users in the REST style

import json
import random
import requests

from restub import Service

# Some list of users
users = [
    {"id": 0, "name": "John Doe"},
    {"id": 1, "name": "Albert Einstein"},
    {"id": 2, "name": "Mahatma Gandhi"}
]

routes = [
    # return the list of all users
    ('GET', r'/user/$', json.dumps(users)),
    # return the random user
    ('GET', r'/user/[0-9]+/$', random.choice(users)),
    # return status code 204
    ('PUT', r'/user/$', None, None, 204),
    # return custom header
    ('DELETE', r'/user/[0-9]+/$', None, {'X-HEADER': 'X-VALUE'})
]

# We run a stub on port 7777 and turn on tracing
with Service(routes=routes, port=7777, trace=True) as srv:
    requests.get('%s/user/' % srv.host)
    requests.get('%s/user/%d/' % (srv.host, 99))
    requests.put('%s/user/' % srv.host, json={'name': 'James Bond'})
    requests.delete('%s/user/%d/' % (srv.host, 100))

Result of tracing:

[time] Service: 7777 is running at http://localhost:7777

[time] Method GET "/user/", status: 200
Request headers: Response headers:
⚫ Host: localhost: 7777                      ⚪ Server: Restub Service
⚫ Accept: */*                                ⚪ Date: [datetime] GMT
⚫ User-Agent: python-requests                ⚪ Content-type: application/json
⚫ Accept-Encoding: gzip, deflate             ⚪ Content-length: 106
⚫ Connection: keep-alive

[time] Method GET "/user/99/", status: 200
Request headers: Response headers:
⚫ Host: localhost: 7777                      ⚪ Server: Restub Service
⚫ Accept: */*                                ⚪ Date: [datetime] GMT
⚫ User-Agent: python-requests                ⚪ Content-type: application/json
⚫ Accept-Encoding: gzip, deflate             ⚪ Content-length: 35
⚫ Connection: keep-alive

[time] Method PUT "/user/", status: 204
Request headers: Response headers:
⚫ Host: localhost: 7777                       ⚪ Server: Restub Service
⚫ Accept: */*                                 ⚪ Date: [datetime] GMT
⚫ User-Agent: python-requests
⚫ Accept-Encoding: gzip, deflate
⚫ Connection: keep-alive
⚫ Content-Length: 15
⚫ Content-Type: application/x-www-form-urlencoded
⤇ Payload: b'{"name": "James Bond"}'

[time] Method DELETE "/user/100/", status: 200
Request headers: Response headers:
⚫ Host: localhost: 7777                     ⚪ Server: Restub Service
⚫ Accept: */*                               ⚪ Date: [datetime] GMT
⚫ User-Agent: python-requests               ⚪ X-HEADER: X-VALUE
⚫ Accept-Encoding: gzip, deflate
⚫ Connection: keep-alive
⚫ Content-Length: 0

[time] Service: 7777 was stopped

Running the tests

docker-compose  up - d
docker exec -it restub tox

License

This project is licensed under the MIT License - see the LICENSE file for details

Join

Any suggestion and help would be welcome! Get on board! 😏 ✌️

About

RESTUB is a library for creating HTTP stubs

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages