REST has gained lot of popularity and is virtually the default architectural style for designing and implementing RESTful web services. Wikipedia states:
Representational state transfer (REST) or RESTful Web services are one way of providing interoperability between computer systems on the Internet. REST-compliant Web services allow requesting systems to access and manipulate textual representations of Web resources using a uniform and predefined set of stateless operations.
Implementing REST APIs in Flask is relatively easy. As this week’s challenge is a House Inventory Tracker, lets do CRUD on room items.
Note this post uses the simplest possible example, and focuses on the testing. I use an in-memory list for storage. In real life you probably want a DB, although you could also use a Google Spreadsheet and Python. Security / authentication is critical too, see Miguel Grinberg’s excellent Designing a RESTful API with Python and Flask how to implement that.
Get ready
First create a virtualenv and do pip install flask
API code and endpoints
To create a simple API you implement one or more HTTP methods, in this case the following endpoints:
@app.route('/api/v1.0/items', methods=['GET'])
@app.route('/api/v1.0/items/', methods=['GET'])
@app.route('/api/v1.0/items', methods=['POST'])
@app.route('/api/v1.0/items/', methods=['PUT'])
@app.route('/api/v1.0/items/', methods=['DELETE'])
Full code here.
Testing part I) – manually with curl
I first put some curl commands in a test script, isn’t it cool you can just use curl to test your new shiny API?
$ python curl.py
# get items
curl -i http://127.0.0.1:5000/api/v1.0/items
HTTP/1.0 200 OK
...
{
"items": [
{
"id": 1,
"name": "laptop",
"value": 1000
},
...
...
# add item with proper values
curl -i -H "Content-Type: application/json" -X POST -d '{"name":"monitor", "value": 200}' http://127.0.0.1:5000/api/v1.0/items
HTTP/1.0 201 CREATED
...
{
"item": {
"id": 4,
"name": "monitor",
"value": 200
}
}
...
...
It surely is! However you have to read the output every time you test. Not cool 🙁
Testing part II) – automation with unittest
Here the whole exercise became pretty interesting, how to unit test an API?!
Flask facilitates a nice method you can use in your setUp (= repeats for each unit test):
test_client(use_cookies=True, **kwargs)
Creates a test client for this application. For information about unit testing head over to Testing Flask Applications.
You can test response codes and of course see how the data (list of items in this case) changes after each request.
The only challenge was the isolation of each unit test: I had to do copy the app.items to a backup variable in setUp (a deepcopy to not leave references around) and pass it back in tearDown. Similarly for a DB back-end you would probably construct and drop a test table to have a clean slate for every test.
The full unit tests are here, summary:
def test_get_all(self): ...
def test_get_one(self): ...
def test_item_not_exist(self): ...
def test_post(self): ...
def test_update(self): ...
def test_update_error(self): ...
def test_delete(self): ...
This whole exercise took me some time but it was great learning, not something you get from just reading about it! And this can serve as a template when testing other APIs.
next(API)
Since listening to Soft Skills I want to do some time logging to increase my productivity.
What if I can have a simple API where I can send log entries of ‘deep work time x spent on activity y’ via a Slack Webhook to a DB or earlier mentioned Google Spreadsheet?
At the time of writing this article I stumbled upon Flask-RESTful which should make this even easier (more elegant). So stay tuned for a part II …
Keep Calm and Code in Python!
— Bob