Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,36 @@
# api-tests-python

This repository contains a small collection of pytest tests used to
exercise various public APIs such as **reqres.in**,
**jsonplaceholder.typicode.com** and **fakestoreapi.com**. The tests
are intentionally simple and can be used as a starting point for API
automation examples.

## Setup

Create a virtual environment and install the dependencies:

```bash
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```

## Running the tests

Execute all tests using `pytest` from the repository root:

```bash
pytest -v
```

The tests make real HTTP requests to public APIs so internet access is
required.

## Configuration

The base URLs for the APIs reside in `config/config.json`. `RequestHandler`
reads these values to build the full request URLs.

Sample payloads can be found under `data/` and are loaded within the
test modules when required.
7 changes: 6 additions & 1 deletion config/config_reader_api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
"""Helper for reading API configuration files."""

import json


def read_config():
"""Load configuration values from ``config.json``."""
with open("config/config.json", "r") as f:
return json.load(f)

CONFIG = read_config()

CONFIG = read_config()
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pytest
requests
pytest
requests
jsonschema
168 changes: 87 additions & 81 deletions tests/test_jsonplaceholder.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,87 @@
import pytest
from utils.request_handler import RequestHandler
from utils.assertions import assert_status_code, assert_json_key
from utils import logger

handler = RequestHandler(base_url_key="jsonplaceholder")
log = logger.log



def test_get_todo_by_id():
response = handler.get("/todos/1")

log.info(f"GET /todos/1 → {response.status_code}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 200)

for key in ["userId", "id", "title", "completed"]:
assert_json_key(response, key)


def test_get_all_posts():
response = handler.get("/posts")

log.info(f"GET /posts → {response.status_code}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 200)

data = response.json()
assert isinstance(data, list), "Response is not a list"
if data:
for key in ["userId", "id", "title", "body"]:
assert key in data[0], f"Key '{key}' not found in first post"


def test_create_post():
payload = {
"title": "foo",
"body": "bar",
"userId": 1
}
response = handler.post("/posts", json_data=payload)

log.info(f"POST /posts → {response.status_code}")
log.info(f"Payload: {payload}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 201)
# Check for expected keys in response
for key in ["id", "title", "body", "userId"]:
assert_json_key(response, key)


def test_update_post():
payload = {
"id": 1,
"title": "foo",
"body": "bar",
"userId": 1
}
response = handler.put("/posts/1", json_data=payload)

log.info(f"PUT /posts/1 → {response.status_code}")
log.info(f"Payload: {payload}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 200)
# Check for expected keys in response
for key in ["id", "title", "body", "userId"]:
assert_json_key(response, key)


def test_delete_post():
response = handler.delete("/posts/1")

log.info(f"DELETE /posts/1 → {response.status_code}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 200) # Or 204 if that’s the expected status
import pytest
from utils.request_handler import RequestHandler
from utils.assertions import assert_status_code, assert_json_key
from utils.schema_validator import validate_json_schema
from utils.schemas import TODO_SCHEMA, POST_SCHEMA
from utils import logger

handler = RequestHandler(base_url_key="jsonplaceholder")
log = logger.log


def test_get_todo_by_id():
"""Validate that a single TODO item matches the expected schema."""
response = handler.get("/todos/1")

log.info(f"GET /todos/1 → {response.status_code}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 200)
validate_json_schema(response.json(), TODO_SCHEMA)
for key in ["userId", "id", "title", "completed"]:
assert_json_key(response, key)


def test_get_all_posts():
"""Ensure the posts endpoint returns a list of posts."""
response = handler.get("/posts")

log.info(f"GET /posts → {response.status_code}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 200)

data = response.json()
assert isinstance(data, list), "Response is not a list"
if data:
for key in ["userId", "id", "title", "body"]:
assert key in data[0], f"Key '{key}' not found in first post"


def test_create_post():
"""Create a new post and validate the response schema."""
payload = {
"title": "foo",
"body": "bar",
"userId": 1,
}
response = handler.post("/posts", json_data=payload)

log.info(f"POST /posts → {response.status_code}")
log.info(f"Payload: {payload}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 201)
validate_json_schema(response.json(), POST_SCHEMA)
for key in ["id", "title", "body", "userId"]:
assert_json_key(response, key)


def test_update_post():
"""Update an existing post and verify the returned payload."""
payload = {
"id": 1,
"title": "foo",
"body": "bar",
"userId": 1,
}
response = handler.put("/posts/1", json_data=payload)

log.info(f"PUT /posts/1 → {response.status_code}")
log.info(f"Payload: {payload}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 200)
validate_json_schema(response.json(), POST_SCHEMA)
for key in ["id", "title", "body", "userId"]:
assert_json_key(response, key)


def test_delete_post():
"""Delete an existing post."""
response = handler.delete("/posts/1")

log.info(f"DELETE /posts/1 → {response.status_code}")
log.info(f"Response Body: {response.text}")

assert_status_code(response, 200)
Loading