5 minute read

Python and requests and header

Logos in header image sources: Python, Requests, JSON, HTTP

This is second post in a series on how to build an API framework using python.

Read part 1 here

Understanding API under test

Letโ€™s understand the API that we will be using in this tutorial a bit better.

We will be using people-api which is a set of CRUD HTTP operations developed using Python Flask, SQLAlchemy and uses sqlite as the database and represents a list of persons with first name, last name and an id

To setup clone people-api repo from github, cd to the newly cloned repo and then activate the pipenv by running below commands:

git clone git@github.com:automationhacks/course-api-framework-python.git
cd people-api
pipenv shell

Ensure all the dependencies are installed in the pipenv by executing:

pipenv install

Once done, Init the database by executing

python build_database.py

This would seed the local sqlite database with some dummy records

Finally then start the HTTP service on your local by executing and ensuring this keeps on running during the test

python server.py

We can see from the log that the local service has started, you would also see logs for any request made to the server

* Serving Flask app "config" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 105-008-664

Letโ€™s navigate to the default host and port printed in flask logs to see the application,

Note: To see the swagger spec for this API we can navigate to

Important Note ๐Ÿ“•: On a windows machine, you may want to replace with Read this post on How-To Geek to understand this is needed

As you can see this API supports basic CRUD operations (CREATE, READ, UPDATE, DELETE)

Swagger preview

If you want to play around with these requests, I have also included a postman collection inside this repo under postman folder with all these basic operations already setup.

The API supports creating a person, updating him/her, deleting the person and able to read a user or all the users in the database. We will see these operations while writing tests.

First test, read via GET API

Letโ€™s see how we can create a basic test to make this HTTP request via python using requests module that we have already imported

The curl for this API is:

curl --location --request GET '' \
--header 'Accept: application/json'

Below is the code snippet that wires up a basic HTTP GET request and then asserts that the service returns us a 200 status code and that the returned list of people has kent in it.

Notice we use assert_that from assertpy module for fluent assertions, The failure messages from these are quite readable and we would discuss more about this in a future post. Please ensure you have that installed in your virtualenv using pipenv install assertpy

import requests
from assertpy.assertpy import assert_that
from config import BASE_URI

def test_read_all_has_kent():
    # We use requests.get() with url to make a get request
    response = requests.get(BASE_URI)
    # response from requests has many useful properties
    # we can assert on the response status code
    # We can get python dict as response by using .json() method
    response_text = response.json()
    first_names = [people['fname'] for people in response_text]

In above, we are using BASE_URI variable to get the base api url to be used. This is stored in a config.py file as below:


Creating a person using POST method

Alright, now that we have the basic case of GET API taken care of, letโ€™s see how we can make a POST request.

Generally in Rest APIโ€™s when you need to create a resource you would use POST method

Below is the cURL to make the request, our python code is going to provide the same headers, body and URL to hit the request

curl --location --request POST '' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data-raw '{
   "fname": "TAU",
   "lname": "User"

Here is the complete flow that we want to automate:

  • Create a new person in the db
  • Verify if the newly created user is present using the GET API
  • Filter to find if the created data is present in all the users in the system

Here Ln 2: calls create_new_person() where we first create the body to be passed as part of the request

Notice on Ln 14 we are using dumps() method from json module to convert python dict to json string (serialization) and using str(uuid4()) to get a unique last name on Ln 13 to ensure we donโ€™t have conflicting data on every test run

Finally we make the POST request using

response = requests.post(url=BASE_URI, data=payload, headers=headers)

And then use search_created_user_in() method to see if the newly created user is present in the database

Deleting a person using DELETE method

How would we go about the delete method?

In this API we can delete a person by passing in the person_id, it is always a good practice to create your own test data before trying delete operations instead of using any existing data since that could result in non-deterministic tests

So here it the flow:

  • Create a new person
  • Get the person id using read API
  • Hit the delete endpoint and assert

Since we already have methods to create and then read users, the only thing left is to implement the DELETE operation by ensuring the person_id is passed in the API path params, We use the fantastic f-strings to create our URL and then hit delete using requests.delete() method.

def test_created_person_can_be_deleted():
    persons_last_name = create_new_person()

    peoples = requests.get(BASE_URI).json()
    newly_created_user = search_created_user_in(peoples, persons_last_name)[0]

    delete_url = f'{BASE_URI}/{newly_created_user["person_id"]}'
    response = requests.delete(delete_url)


Note in assertions we are not specifying numeric value for the status codes, requests provides a lookup dictionary wherein you can easily specify the code using readable english like syntax and this is definitely better than parsing what code X means on MDN - Mozilla developer network every time ๐Ÿ˜‰

In other words instead of 200 we could write it as requests.codes.ok



This is just a taste of what requests as a library can do. For more details on what all features it provides please read requests docs and I hope this gives you a good idea on how to use requests to make HTTP calls. In the next post we would discuss more on working with JSON/XML data types and assertions. Stay tuned. โฐ

You can find the complete code for this course on Github at automationhacks/course-api-framework-python

And here are reads from MDN on Content-Type and Accept headers

If you found this post useful, Do share it with a friend or colleague and if you have thoughts, Iโ€™d be more than happy to chat over at twitter or comments. Until next time. Happy Testing and Coding.