Mocking

The mock client that comes with RestORM is an excellent method to simulate server side responses that you expect from a real REST API. You typically use it in your unit tests.

You can switch out your client with a mock client and simply work as usual, given that you mocked some typical responses. The mocking works transparently with Resource classes. Just pass your mock client to it.

Where you would normally have:

from restorm.resource import Resource

class Book(Resource):
    class Meta:
        item = r'^book/(?P<isbn>\w+)$'

>>> from restorm.clients.jsonclient import JSONClient
>>> client = JSONClient(root_uri='http://www.example.com/api/')

>>> book = Book.objects.get(isbn='978-1441413024', client=client)
>>> book.data['title']
u'Dive into Python'

You can replace it all with mocking behaviour that does not rely on actual communication with the external API:

from restorm.clients.mockclient import MockApiClient
mock_client = MockApiClient(
    root_uri='http://www.example.com/api/',
    responses={
        'book/978-1441413024': {
            'GET': ({'Status': 200}, {'title': 'Dive into Python'})
        }
    }
)

>>> book = Book.objects.get(isbn='978-1441413024', client=mock_client)
>>> book.data['title']
u'Dive into Python'

There are several approaches.

Mocking single predefined responses

You can mock a simple specific response in small tests using the MockResponse class and subclasses, and MockClient. You typically use these classes where you do not want to mock an entire API and can suffice with just a few responses that don’t happen irregularly.

class restorm.clients.mockclient.MockResponse(headers, content)

Main class for mocked responses. Headers can be provided as dict. The content is simply returned as response and is usually a string but can be any type of object.

>>> from restorm.clients.mockclient import MockResponse
>>> response = MockResponse({'Status': 200}, {'foo': 'bar'})
>>> response.headers
{'Status': 200}
>>> response.content
{'foo': 'bar'}
class restorm.clients.mockclient.StringResponse(headers, content)

A response with stringified content.

>>> from restorm.clients.mockclient import StringResponse
>>> response = StringResponse({'Status': 200}, '{}')
>>> response.content
'{}'
class restorm.clients.mockclient.FileResponse(headers, filepath)

A response with the contents of a file, read by absolute file path.

>>> from restorm.clients.mockclient import FileResponse
>>> response = FileResponse({'Status': 200}, 'response.json')

Using the above classes, you can pass desired responses to your mock client.

class restorm.clients.mockclient.MockClient(*args, **kwargs)

A mock client, emulating the rest client. The client returns predefined responses. You can add any MockResponse sub class to the responses argument.

It doesn’t matter what URI you request or what data you pass in the body, the first response you added is just returned on the first request.

Responses are popped from a queue. This means that if you add 3 responses, you can only make 3 requests before a ValueError is raised.

>>> from restorm.clients.mockclient import MockClient, StringResponse
>>> desired_response = StringResponse({'Status': 200}, '{}')
>>> mock_client = MockClient('http://mockserver/', responses=[desired_response,])
>>> response = mock_client.get('/') # Can be any URI.
>>> response.content
u'{}'
>>> response.status_code
200
>>> response = mock_client.get('/') # Another call.
ValueError: Ran out of responses when requesting: /

Mocking entire servers

If you are going to test alot against a certain API (your own, or an external one), it might be a good idea to make mock the a part of the API. You typically use this to make functional tests or broader unit tests. You can use the MockApiClient for this purpose.

You can even create a web server from a MockApiClient instance to “browse” through your mock API using a browser or keep it running to let your application talk to it.

class restorm.clients.mockclient.MockApiClient(*args, **kwargs)

A client that emulates communicating with an entire mock API.

Specify each resource and some headers and/or content to return. You can use a tuple as response containing the headers and content, or use one of the available MockResponse (sub)classes to return the contents of a string or file.

The structure of the responses is:

{<relative URI>: {<HTTP method>: ({<header key>: <header value>, ...}, <response content>)}}
>>> from restorm.clients.mockclient import MockApiClient, StringResponse
>>> mock_api_client = MockApiClient(responses={
...     'book/': {
...         'GET': ({'Status': 200}, [{'id': 1, 'name': 'Dive into Python', 'resource_url': 'http://localhost/api/book/1'}]),
...         'POST': ({'Status': 201, 'Location': 'http://localhost/api/book/2'}, ''),
...     },
...     'book/1': {'GET': ({'Status': 200}, {'id': 1, 'name': 'Dive into Python', 'author': 'http://localhost/api/author/1'})},
...     'author/': {'GET': ({'Status': 200}, [{'id': 1, 'name': 'Mark Pilgrim', 'resource_url': 'http://localhost/api/author/1'}])},
...     'author/1': {'GET': FileResponse({'Status': 200}, 'response.json')}
... }, root_uri='http://localhost/api/')
...
>>> response = mock_api_client.get('http://localhost/api/book/1')
>>> response.content
{'id': 1, 'name': 'Dive into Python', 'author': 'http://localhost/api/author/1'}
>>> response.status_code
200
get_response_from_request(request)

You may override this method to implement your own response logic based on given request. You can even modify the self.responses based on some POST, PUT or DELETE request.

This is the only method that looks at self.responses. Therefore, overriding this method also allows you to create a custom format for this container variable or even mutate the responses variable based on the request.

create_server(ip_address, port, handler=None)

Creates a server instance and returns it. The server instance has access to this mock to provide the responses.

>>> from restorm.clients.mockclient import MockApiClient, StringResponse
>>> mock_api_client = MockApiClient(responses={'/': {'GET': ({'Status': 200}, 'My homepage')}})
>>> server = mock_api_client.create_server('127.0.0.1', 8000)
>>> server.serve_forever()

Example: Library API

An extensive example is given in the restorm.examples.mock module that extends the mock API from the Tutorial.

Note

This example is also used in internal unit tests. The MockApiClient and related classes are specifically made for the purpose of unit testing, or “playground” testing.

The example here is a JSON webservice. You can instantiate it and perform requests from the console:

>>> from restorm.examples.mock.api import LibraryApiClient
>>> client = LibraryApiClient()
>>> response = client.get('author/1')
>>> response.raw_content
'{"books": [{"resource_url": "http://localhost/api/book/978-1441413024", "isbn": "978-1441413024", "title": "Dive into Python"}], "id": 1, "name": "Mark Pilgrim"}'

You can also start it as a server and connect to it with your browser, or let your application connect to it:

$ python -m restorm.examples.mock.serv 127.0.0.1:8000

Shut it down with CTRL-C. The above Python script basically does:

>>> server = client.create_server()
>>> server.serve_forever()

Expanding on what’s there

Both the MockClient and MockApiClient constist of a base class and a mixin that handles the specifics. The MockClient class is defined as:

from restorm.clients.base import ClientMixin
from restorm.clients.mockclient import BaseMockClient

class MockClient(BaseMockClient, ClientMixin):
    pass

You can easily use these classes for your own use. Actually, creating a mock client that serves JSON is nothing more than:

from restorm.clients.jsonclient import JSONClientMixin

class MockJSONClient(BaseMockClient, JSONClientMixin):
    pass

With the MockApiClient you can also use these mixins:

from restorm.clients.jsonclient import JSONClientMixin, json
from restorm.clients.mockclient import BaseMockApiClient

class MockApiJSONClient(BaseMockApiClient, JSONClientMixin):
    pass

Project Versions

Table Of Contents

Previous topic

Resources

This Page