Simple API Testing with  Pytest and Requests module using Python

Simple API Testing with Pytest and Requests module using Python

Introduction

If you have been following my last blog on Build A Test Automation Framework with me from Scratch, we will use that framework that we have built to work with our api test. The only different is instead of using Selenium Library we will use Requests Library, and for generating logs and reports are the same.

However we will still start from the beginning, to make you sure we won't get lost.

Get Started

Installation

Python Library required:

pip install pytest
pip install requests
pip install pytest-html
pip install jsonpath

Test Cases

Let's create our first API test cases. Our first test case is to Get list of user from this API endpoint https://reqres.in/api/users?page=2 and verify the response code is 200 ok and page in response body is page number 2.

To do this:

  1. Create a Python Package testCases

  2. Under testCases Package, create a test file test_GetListUser.py

  3. In test_GetListUser.py add the following code:

import json, jsonpath
import requests

baseURL = "https://reqres.in/api/users?page=2"


# Test 01 make sure response code is 200 ok
def test_response_code_200():

    # GET request to our api endpoint
    response = requests.get(baseURL)

    # Check if response code is 200 then passed our test
    if response.status_code == 200:
        assert True
    # if not 200 failed our test
    else:
        assert False


# Test 02 make sure response page is page number 2
def test_page_number_is_2():

    # GET request to our api endpoint
    response = requests.get(baseURL)

    # Check if response code is 200
    if response.status_code == 200:
        # get response body in json format
        json_response = json.loads(response.text)
        # get page param from response body
        page = jsonpath.jsonpath(json_response, "page")

        # the result will be a list, so use index to compare the result
        # check page response is page number 2, then passed our test
        if page[0] == 2:
            assert True
        # otherwise failed our test
        else:
            assert False

    # if status code response with anything beside 200 failed our test
    else:
        assert False
Run Test Cases

To run our test cases, open terminal or command prompt and change directory to our test cases working directory type the following cmd:

# type pytest following by test filename
pytest test_GetListUser.py

Generate Logs

Let's add logs to our test cases. To Generate Log and add to our test cases:

  1. Create a Python Package utilities

  2. Create a folder Log to store logs from our test cases

  3. Under utilities package, create a logger file logger.py to generate logs

  4. In logger.py add the following code:

import logging
import sys

class LogGen:
    @staticmethod
    def genlog():
        logger = logging.getLogger()
        logger.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s | %(levelname)s | %(message)s',
                                      '%m/%d/%Y %I:%M:%S %p')

        stdout_handler = logging.StreamHandler(sys.stdout)
        stdout_handler.setLevel(logging.DEBUG)
        stdout_handler.setFormatter(formatter)

        # generate logging and store in logs.log file under log folder
        file_handler = logging.FileHandler('../Log/logs.log')
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(formatter)

        logger.addHandler(file_handler)
        logger.addHandler(stdout_handler)
        return logger
Add Logs to Test Cases

After we have code to generate logs, let's add logs to our test cases: Don't forget to import logger from utilities package first

import json, jsonpath
import requests
from utilities.logger import LogGen

# GenerateLog Object
generateLog = LogGen.genlog()

baseURL = "https://reqres.in/api/users?page=2"


# Test 01 make sure response code is 200 ok
def test_response_code_200():
    generateLog.info("========================= Begin Test Response Code =========================")

    # GET request to our api endpoint
    generateLog.info("GET Requesting to URL %s", baseURL)
    response = requests.get(baseURL)

    # Check if response code is 200 then passed our test
    if response.status_code == 200:
        generateLog.info("Status Code Response %s", response.status_code)
        generateLog.info("Response Body: \n %s", json.loads(response.text))
        generateLog.info("Test Case PASSED")
        generateLog.info("========================= END Test Response Code =========================")
        assert True
    # if not 200 failed our test
    else:
        generateLog.warning("Status Code Response %s", response.status_code)
        generateLog.error("Test Case FAILED")
        generateLog.info("========================= END Test Response Code =========================")
        assert False


# Test 02 make sure response page is page number 2
def test_page_number_is_2():
    generateLog.info("========================= Begin Test Page Number =========================")

    # GET request to our api endpoint
    generateLog.info("GET Requesting to URL %s", baseURL)
    response = requests.get(baseURL)

    # Check if response code is 200
    if response.status_code == 200:
        generateLog.info("Status Code Response %s", response.status_code)

        # get response body in json format
        json_response = json.loads(response.text)
        generateLog.info("Response Body: \n %s", json_response)
        # get page param from response body
        page = jsonpath.jsonpath(json_response, "page")

        # the result will be a list, so use index to compare the result
        # check page response is page number 2, then passed our test
        if page[0] == 2:
            generateLog.info("Response Page number: %s", page[0])
            generateLog.info("Test Case PASSED")
            generateLog.info("========================= Begin Test Page Number =========================")
            assert True
        # otherwise failed our test
        else:
            generateLog.info("Response Page number: %s", page[0])
            generateLog.info("Test Case FAILED")
            generateLog.info("========================= END Test Page Number =========================")
            assert False

    # if status code response with anything beside 200 failed our test
    else:
        generateLog.info("Status Code Response: %s", response.status_code)
        generateLog.info("Test Case FAILED")
        generateLog.info("========================= END Test Page Number =========================")
        assert False

Run test again, we will get logs in Log/logs.log file like this:

10/28/2022 01:07:52 PM | INFO | ========================= Begin Test Response Code =========================
10/28/2022 01:07:52 PM | INFO | GET Requesting to URL https://reqres.in/api/users?page=2
10/28/2022 01:07:52 PM | INFO | Status Code Response 200
10/28/2022 01:07:52 PM | INFO | Response Body: 
 {'page': 2, 'per_page': 6, 'total': 12, 'total_pages': 2, 'data': [{'id': 7, 'email': 'michael.lawson@reqres.in', 'first_name': 'Michael', 'last_name': 'Lawson', 'avatar': 'https://reqres.in/img/faces/7-image.jpg'}, {'id': 8, 'email': 'lindsay.ferguson@reqres.in', 'first_name': 'Lindsay', 'last_name': 'Ferguson', 'avatar': 'https://reqres.in/img/faces/8-image.jpg'}, {'id': 9, 'email': 'tobias.funke@reqres.in', 'first_name': 'Tobias', 'last_name': 'Funke', 'avatar': 'https://reqres.in/img/faces/9-image.jpg'}, {'id': 10, 'email': 'byron.fields@reqres.in', 'first_name': 'Byron', 'last_name': 'Fields', 'avatar': 'https://reqres.in/img/faces/10-image.jpg'}, {'id': 11, 'email': 'george.edwards@reqres.in', 'first_name': 'George', 'last_name': 'Edwards', 'avatar': 'https://reqres.in/img/faces/11-image.jpg'}, {'id': 12, 'email': 'rachel.howell@reqres.in', 'first_name': 'Rachel', 'last_name': 'Howell', 'avatar': 'https://reqres.in/img/faces/12-image.jpg'}], 'support': {'url': 'https://reqres.in/#support-heading', 'text': 'To keep ReqRes free, contributions towards server costs are appreciated!'}}
10/28/2022 01:07:52 PM | INFO | Test Case PASSED
10/28/2022 01:07:52 PM | INFO | ========================= END Test Response Code =========================
10/28/2022 01:07:52 PM | INFO | ========================= Begin Test Page Number =========================
10/28/2022 01:07:52 PM | INFO | GET Requesting to URL https://reqres.in/api/users?page=2
10/28/2022 01:07:52 PM | INFO | Status Code Response 200
10/28/2022 01:07:52 PM | INFO | Response Body: 
 {'page': 2, 'per_page': 6, 'total': 12, 'total_pages': 2, 'data': [{'id': 7, 'email': 'michael.lawson@reqres.in', 'first_name': 'Michael', 'last_name': 'Lawson', 'avatar': 'https://reqres.in/img/faces/7-image.jpg'}, {'id': 8, 'email': 'lindsay.ferguson@reqres.in', 'first_name': 'Lindsay', 'last_name': 'Ferguson', 'avatar': 'https://reqres.in/img/faces/8-image.jpg'}, {'id': 9, 'email': 'tobias.funke@reqres.in', 'first_name': 'Tobias', 'last_name': 'Funke', 'avatar': 'https://reqres.in/img/faces/9-image.jpg'}, {'id': 10, 'email': 'byron.fields@reqres.in', 'first_name': 'Byron', 'last_name': 'Fields', 'avatar': 'https://reqres.in/img/faces/10-image.jpg'}, {'id': 11, 'email': 'george.edwards@reqres.in', 'first_name': 'George', 'last_name': 'Edwards', 'avatar': 'https://reqres.in/img/faces/11-image.jpg'}, {'id': 12, 'email': 'rachel.howell@reqres.in', 'first_name': 'Rachel', 'last_name': 'Howell', 'avatar': 'https://reqres.in/img/faces/12-image.jpg'}], 'support': {'url': 'https://reqres.in/#support-heading', 'text': 'To keep ReqRes free, contributions towards server costs are appreciated!'}}
10/28/2022 01:07:52 PM | INFO | Response Page number: 2
10/28/2022 01:07:52 PM | INFO | Test Case PASSED
10/28/2022 01:07:52 PM | INFO | ========================= Begin Test Page Number =========================

Generate HTML Report for Test Result

To Generate an HTML Report is so simple, by just run our test cmd with --html. But let's create a folder Reports to store our HTML reports then run test with the following command:

# pytest --html=<location you want to store report> follow by test filename
pytest --html=../Reports/report.html test_GetListUser.py

Screenshot from 2022-10-28 13-41-52.png

Note: Please refer to the Last blog mentioned at the beginning on how to modified our report.

Test with Other HTTP Method

POST Method

Let's Test Create User with POST request to https://reqres.in/api/users with request body:

{
    "name": "morpheus",
    "job": "leader"
}

and verify it response with Status code 201 created and response body contain id and createdAt parameter. Create another test test_CreateUser.py:

import json, jsonpath
import requests
from utilities.logger import LogGen
generateLog = LogGen.genlog()

base_url = 'https://reqres.in/api/users'


def test_create_user_response_code():
    generateLog.info("===================== Begin Create User Test =============================")
    request_body = {
                "name": "morpheus",
                "job": "leader"
                }

    response = requests.post(base_url, request_body)

    if response.status_code == 201:
        generateLog.info("Status Code Response %s", response.status_code)
        json_response = json.loads(response.text)

        # check if id and createdAt parameter is returned
        if "id" in json_response and "createdAt" in json_response:
            generateLog.info("Response body: \n %s", json_response)
            generateLog.info("id and createdAt parameter exist in response body")
            generateLog.info("Test Case PASSED")
            generateLog.info("===================== End Create User Test =============================")
            assert True
        else:
            generateLog.warning("id and createdAt parameter not exist in response body")
            generateLog.info("Response body: \n %s", json_response)
            generateLog.error("Test Case FAILED")
            generateLog.info("===================== End Create User Test =============================")
            assert False

    else:
        generateLog.warning("User failed to created. Status code: %s", response.status_code)
        generateLog.info("Response body: \n %s", json.loads(response.text))
        generateLog.error("Test Case FAILED")
        generateLog.info("===================== End Create User Test =============================")
        assert False
PUT Method

Let's Test Update User with PUT request to https://reqres.in/api/users/2 with request body:

{
    "name": "morpheus",
    "job": "zion resident"
}

and verify it response name with the name in our request body morpheus and job with the job in our request body zion resident. Create another test test_UpdateUser.py:

import json, jsonpath
import requests
from utilities.logger import LogGen
generateLog = LogGen.genlog()

base_url = 'https://reqres.in/api/users/2'


def test_update_user():
    generateLog.info("===================== Begin Update User Test =============================")
    request_body = {
                "name": "morpheus",
                "job": "zion resident"
                }

    response = requests.put(base_url, request_body)

    if response.status_code == 200:
        generateLog.info("Status Code Response %s", response.status_code)
        json_response = json.loads(response.text)
        response_name = jsonpath.jsonpath(json_response, "name")
        response_job = jsonpath.jsonpath(json_response, "job")

        if response_name[0] == request_body["name"] and response_job[0] == request_body["job"]:
            generateLog.info("Response body: \n %s", json_response)
            generateLog.info("name and job Updated correctly")
            generateLog.info("Test Case PASSED")
            generateLog.info("===================== End Update User Test =============================")
            assert True
        else:
            generateLog.warning("name and jog does not updated correctly")
            generateLog.info("Request body: \n %s", request_body)
            generateLog.info("Response body: \n %s", json_response)
            generateLog.error("Test Case FAILED")
            generateLog.info("===================== End Update User Test =============================")
            assert False

    else:
        generateLog.warning("User failed to update. Status code: %s", response.status_code)
        generateLog.info("Response body: \n %s", json.loads(response.text))
        generateLog.error("Test Case FAILED")
        generateLog.info("===================== End Update User Test =============================")
        assert False
PATCH Method

PATCH method do same as PUT, just replace requests.put() with requests.patch() instead.

DELETE Method

Let's Test Delete User with DELETE request to https://reqres.in/api/users/2 and verify it response with status code 204. Create another test test_DeleteUser.py:

import json, jsonpath
import requests
from utilities.logger import LogGen
generateLog = LogGen.genlog()

base_url = 'https://reqres.in/api/users/2'


def test_delete_user():
    generateLog.info("===================== Begin Delete User Test =============================")

    response = requests.delete(base_url)

    if response.status_code == 204:
        generateLog.info("Status Code Response %s", response.status_code)
        generateLog.info("User Deleted Successfully")
        generateLog.info("Test Case PASSED")
        generateLog.info("===================== End Delete User Test =============================")
        assert True

    else:
        generateLog.warning("User failed to Delete. Status code: %s", response.status_code)
        generateLog.error("Test Case FAILED")
        generateLog.info("===================== End Delete User Test =============================")
        assert False
Run all Test Cases with One CMD

Run all test cases under testCases package with a single command :

pytest --html=../Reports/report.html .

We will have a report.html with all of our test cases:

Screenshot from 2022-10-28 16-11-07.png

Conclusion

I would recommend to give a try on my last blog on Build A Test Automation Framework with me from Scratch, it somehow a bit more detail explanation of this Test Framework. However If there is any unclear explanation from me or if I did not explained it properly, feel free to ask me. I will try my best to help with what I could.