Mock objects and APIs

Since I do not have a computer science background (I have a BA in English Lit and an MA in Information Science), I sometimes think I’ve uncovered something entirely new which turns out to be common practice among programmers. The latest such ‘discovery’ is apparently called, by those who know better, a ‘mock object.’ Wikipedia says that a ‘programmer typically creates a mock object to test the behavior of some other object.’ Oh right, that’s exactly what I’ve done. Ok, then.

In the last year I have had to write two applications which accessed an API that did not exist yet. The reasons for this are obscure and perhaps best left unremarked upon, but I did learn something in the process. When building similar applications in the past I had found that it was incredibly useful to, you know, actually have an API to run them against, so it was suggested that I mock up the–currently non-existent–API. In fact, this turned out to be so useful that if ever I have to build another application that accesses an API I will repeat this procedure. A mock API allows you to test any and all responses that might be returned, and actually having the responses to test against is far more productive (at least it was for me) than simply reading about them in the documentation.

Having now read through the Wikipedia page on mock objects, I know that my own mock API is actually more of a ‘fake API.’ This is because I am not really testing the request itself or the submitted data. Instead I simply return a particular HTTP status code along with a bit of sample JSON in the body. Regardless of my indiscretion with the terminology, if you’d like to learn how I generated my mock/fake API, read on.

My mock fake API in Python

Setting up a simple web server is quite easy using Python’s Basic HTTP server. The following code will create a ‘things’ endpoint at which we can GET a particular thing via it’s Id:

import BaseHTTPServer
import re

class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        a = re.compile("\/things\/[a-zA-Z0-9]*")
        if a.match(self.path):
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(open('data.json').read())

server_class = BaseHTTPServer.HTTPServer
httpd = server_class(('', 1234), MyHandler)
httpd.serve_forever()

To test this code you need to also create a sample json file, named ‘data.json,’ in the same folder as the above Python code. Now you can access the URL http://localhost:1234/things/1234, which should return whatever snippet of json you’ve stored in data.json. The regex on line 6 can be altered to accommodate whatever call you wish to emulate. In this case a ‘thing’ Id can be any number of numbers and lower or upper case letters.

It is similarly easy to handle other kinds of requests, such as PUT, POST and HEAD. Here’s a sample POST against the ‘things’ endpoint that returns a HTTP status code of 404 and a snippet of JSON stored as ‘error.json’:

import BaseHTTPServer

class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_POST(self):
        if self.path == '/things':
            self.send_response(404)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write(open('error.json').read())

server_class = BaseHTTPServer.HTTPServer
httpd = server_class(('', 1234), MyHandler)
httpd.serve_forever()

Should you wish to test the content of this POST and make this more of an actual mock object, you can read the contents of the submitted data using the following:

postLen = int(self.headers['Content-Length'])
postData = self.rfile.read(postLen)

Of course, this approach requires the presence of a ‘Content-Length’ header, but there are probably more direct methods you could try. Lastly, I occasionally found it useful to randomly return different status codes:

self.send_response(random.choice([200,404]))

The server code above could certainly be more dynamic, but for my use case it was easy enough to make manual edits to return a different code temporarily, or to alter the response body in some way.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s