Python API Automation Testing Suite with requests, pytest, and pytest‑html
This guide demonstrates how to build a Python API automation testing suite using requests, pytest, and pytest‑html, covering environment setup, project structure, common utilities, test case implementation, advanced assertions, error handling, test data management, and HTML report generation.
In Python, the requests library can be used for API automation testing, and testing frameworks such as unittest or pytest can organize and run test suites.
Installation
pip install requests pytestProject directory structure
project/
│
├── common/ # public methods module
│ └── utils.py # request, assertion utilities
├── conf/ # configuration module
│ └── config.py # environment and base URL
├── data/ # test data module
│ └── test_data.json # input data for test cases
├── log/ # log module
│ └── log.txt # runtime logs
├── report/ # report module
│ └── report.html # generated HTML report
├── test_case/ # test case modules
│ ├── test_login.py # login API tests
│ ├── test_signup.py# signup API tests
│ └── ... # other API tests
└── testsuite.py # test suite runnercommon/utils.py – common functions
import requests
import json
def send_request(method, url, headers=None, params=None, data=None):
response = requests.request(method, url, headers=headers, params=params, data=data)
response.raise_for_status() # raise if status not 200
return response.json()
def assert_response(response_data, expected_key, expected_value):
assert expected_key in response_data, f"Expected key '{expected_key}' not found in response."
assert response_data[expected_key] == expected_value, f"Expected value for '{expected_key}' is '{expected_value}', but got '{response_data[expected_key]}'"conf/config.py – configuration
TEST_ENVIRONMENT = "development"
BASE_URL = "http://localhost:8000/api/"test_case/test_login.py – example test case
import json
from project.common.utils import send_request, assert_response
from project.conf.config import BASE_URL
class TestLogin:
def test_successful_login(self):
url = f"{BASE_URL}login"
data = {"username": "test_user", "password": "test_password"}
response_data = send_request("POST", url, data=json.dumps(data))
assert_response(response_data, "status", "success")
assert_response(response_data, "message", "Logged in successfully.")
def test_invalid_credentials(self):
url = f"{BASE_URL}login"
data = {"username": "invalid_user", "password": "invalid_password"}
response_data = send_request("POST", url, data=json.dumps(data))
assert_response(response_data, "status", "error")
assert_response(response_data, "message", "Invalid credentials.")testsuite.py – organize and run test cases
import pytest
from project.test_case import test_login, test_signup # other test modules
@pytest.mark.parametrize("test_case_module", [test_login, test_signup])
def test_suite(test_case_module):
suite = unittest.TestLoader().loadTestsFromModule(test_case_module)
runner = unittest.TextTestRunner()
results = runner.run(suite)
assert results.wasSuccessful(), "Test suite failed."
if __name__ == "__main__":
pytest.main(["--html=report/report.html", "--self-contained-html"])Run the suite with:
pytest testsuite.pyAdvanced assertions
def assert_in_response(response_data, key, expected_values):
assert key in response_data, f"Expected key '{key}' not found in response."
assert response_data[key] in expected_values, f"Expected value for '{key}' to be one of {expected_values}, but got '{response_data[key]}'"Error handling in send_request
def send_request(method, url, headers=None, params=None, data=None):
try:
response = requests.request(method, url, headers=headers, params=params, data=data)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as http_error:
logging.error(f"HTTP error occurred: {http_error}")
raise http_error
except Exception as e:
logging.error(f"Unexpected error occurred: {e}")
raise eTest data management
# data/test_data.py
LOGIN_TEST_DATA = {
"valid_credentials": {"username": "test_user", "password": "test_password"},
"invalid_credentials": {"username": "invalid_user", "password": "invalid_password"}
}Use the data in test cases:
from project.data.test_data import LOGIN_TEST_DATA
class TestLogin:
def test_successful_login(self):
url = f"{BASE_URL}login"
data = LOGIN_TEST_DATA["valid_credentials"]
response_data = send_request("POST", url, data=json.dumps(data))
assert_response(response_data, "status", "success")
assert_response(response_data, "message", "Logged in successfully.")
def test_invalid_credentials(self):
url = f"{BASE_URL}login"
data = LOGIN_TEST_DATA["invalid_credentials"]
response_data = send_request("POST", url, data=json.dumps(data))
assert_response(response_data, "status", "error")
assert_response(response_data, "message", "Invalid credentials.")Report generation with pytest-html
pip install pytest-htmlConfigure in testsuite.py :
import pytest
from pytest_html_reporter import attach_extra_css, add_context
@pytest.mark.parametrize("test_case_module", [test_login, test_signup])
def test_suite(test_case_module):
# ... run tests ...
if __name__ == "__main__":
pytest.main(["--html=report/report.html", "--self-contained-html"])
attach_extra_css("custom.css")
add_context({"project_name": "My API Test Project"})Running the suite generates a report.html file with the test results.
Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.