How to Test an API with Pytest and Requests

12.24.2021

In previous tutorials, we learned how to build a Full Stack Application with Next.js, FastAPI, and PostgreSQL. In this tutorial, we will learn how to test it with Pytest and Requests.

Writing tests for backend APIs has major benefits. One, they can be used while developing, so instead of manually using a GUI HTTP client to test your API, you can automate it with code. This is helpful when you find yourself repeatedly running a series of requests to test a specific workflow such as user sign up and log in. Second, they can be used to verify the functionality of your API and make sure it is working as expected.

Create nfp-test directory.

$ mkdir nfp-test

Create virtualenv.

$ python -m venv venv
$ . venv/bin/activate

Install pytest and requests.

$ pip install pytest requests python-dotenv

Create a .env.example.

API_URL=http://localhost:8000

Create a .env.

API_URL=http://localhost:8000

Create a .gitignore.

__pycache__
venv
.pytest_cache
.env

Create a config.py.

import os
from dotenv import load_dotenv

load_dotenv()

class Config:
    API_URL = os.getenv("API_URL")

config = Config()

Create a test_notes.py.

import requests
from config import config


def test_get_notes():
    r = requests.get(f'{config.API_URL}/notes/')
    assert r.status_code == 200

def test_post_note():
    payload = {"text": "hello world", "completed": False}
    r = requests.post(f'{config.API_URL}/notes/', json=payload)
    assert r.status_code == 200

Create a test_users.py.

import requests
import string
import random
from config import config

username = None
password = None
token = None

def test_post_users():
    global username
    global password
    username = ''.join(random.sample(string.ascii_letters, 8))
    password = ''.join(random.sample(string.ascii_letters, 8))
    payload = {
        "username": username,
        "password": password
        }
    r = requests.post(f'{config.API_URL}/users/', json=payload)
    assert r.status_code == 200

def test_post_token():
    global token
    payload = {"username": username, "password": password}
    r = requests.post(f'{config.API_URL}/token', data=payload)
    json = r.json()
    token = json['access_token']
    assert r.status_code == 200

def test_get_users_me():
    headers = {'Authorization': f'Bearer {token}'}
    r = requests.get(f'{config.API_URL}/users/me/', headers=headers)
    json = r.json()
    assert json['username'] == username
    assert r.status_code == 200

def test_get_users_me_items():
    headers = {'Authorization': f'Bearer {token}'}
    r = requests.get(f'{config.API_URL}/users/me/items/', headers=headers)
    assert r.status_code == 200

Run the tests.

$ pytest

Run the tests with stdout for all tests.

$ pytest -rA

Conclusion

Congratulations, you have written the first API tests for our NFP boilerplate app. You have used pytest and requests to test the public endpoints as well as the private endpoints. Even light integration tests such as the ones above will go a long way in increasing developer confidence when making changes to a codebase.