RESTful API, HOW TO | Part 3 — Testing

Daniele Dapuzzo
Analytics Vidhya
Published in
4 min readMar 22, 2020

Design and implement services are part of my daily bread, I want to share some best practices and tips that can help you in your work.

In this series on RESTful API, I will discuss several topics:

  • Design
  • Implementation
  • Testing
  • Deployment

Some information

We will use swagger editor to design our APIs, python language to create the microservice and finally Docker to deliver the final solution. All the code is available in this repo.

In the previous articles we saw how to design a simple RESTful API and how to implement it. It’s now time to add some tests, but first let’s talk about tests, what they are useful for and why you should always add them to your development flow.

In this article we will talk about unit tests, they are useful to test single components of the application. They can help you to find bugs before going to production, if you write them in the right way they can also check that a functionality still works after a change of the code. We are now considering tests after we have already developed almost all the code. There are also pattern like TDD in which you first write the tests, run them (they will fail) and finally you write the code to make the test to pass.

The code generated from swagger already contains some tests, if we go to tests folder we can see something like this:

from swagger_server.test import BaseTestCase


class TestTodoController(BaseTestCase):
"""TodoController integration test stubs"""

def test_create_todo(self):
"""Test case for create_todo

Create a new to-do
"""
body = Todo()
response = self.client.open(
"/v1/todo",
method="POST",
data=json.dumps(body),
content_type="application/json",
)
self.assert200(response, "Response body is : " + response.data.decode("utf-8"))

These test stubs are useful to test the interaction between the clients and the APIs we have just created. We will not focus on these tests because we have a DB to interact with so we cannot say that these tests are unit tests, in fact their results depends both on HTTP communication and DB availability. These tests could be triggered in a integration test environment.

Writing tests

Let’s create a new file in the same directory, we will create all the test cases that check that our DAO methods will work, but first of doing so, let’s change a little the BaseTestCase class in order to make it work with the DB.

import logging
import os

from flask_testing import TestCase

from swagger_server.__main__ import create_app


class BaseTestCase(TestCase):

os.environ["APPLICATION_ENV"] = "Testing"

def create_app(self):
logging.getLogger("connexion.operation").setLevel("ERROR")
app = create_app()

return app.app

Thanks to the configuration of our application we could only change the APPLICATION_ENV variable and we are now ready to write our first tests:

Below there is the first test, it is an example on how create a case that tests the creation of a record inside the database. The flow that we are using is the following:

  • we first create a test object
  • we save it in the DB using the DAO method that we implemented earlier,
  • we query the database with a SQL raw query in order to not use other methods of our code
  • we check that the record has been saved correctly
class TestDaoController(BaseTestCase):

def setUp(self):
with self.app.app_context():
db.create_all()

def tearDown(self):
with self.app.app_context():
db.session.remove()
db.drop_all()

def test_save_todo_create_a_record_inside_db(self):
test_todo = TodoModel(
name="test_name",
description="some desc",
due_date=datetime.utcnow(),
status=Status("to do")
)
test_todo.save()
raw = text("select * from todo where name = '{}'".format("test_name"))
result = db.session.execute(raw)
results = []
for r in result:
results.append(r)
self.assertTrue(r["name"] == "test_name")
self.assertTrue(len(results) == 1)

The two methods setUp and tearDown are noteworthy because they run respectively before and after each test case, ensuring that each case has an empty and clean database in order to make every test case independent.

For the complete series of tests please check the repository.

Running tests

It is now time to run tests, thanks to the generated code we already have an environment almost ready to run.

If we have correctly activated the virtual environment and installed in it all the requirements we just need to type tox inside the terminal and run it.

We will see the results of the tests.

Some considerations

Unit testing is a huge part of the development, it has multiple shades and it woul be wrong to try to cover all the aspects of this topic inside this series of article, because of its complexity, indeed it deserves a dedicated series.

What is fundamental to catch from this article is the importance to write tests for your application, because they can save hours of debugging, mainly if the codebase is shared to other people working at it.

Some useful links for testing with python are:

In this article we introduced the topic of unit tests, how you can write them and how to run them in a proper environment, I encourage you to explore this topic, because it seems hard and actually sometimes it is, but is a practice that a developer should be able to master.

In the next and final article we will see the deployment in a production-like environment of our application.

REMINDER: you can find all the updated code at this GitHub repository!

Link to the previous Article: https://medium.com/analytics-vidhya/restful-api-how-to-part-2-implementation-e3bca6072b70

--

--

Daniele Dapuzzo
Analytics Vidhya

Software Engineer, interested in new technologies, travel lover.