Mastering API Request Wrappers in Pytest: Reusable GET, POST, PUT, DELETE
Learn how to boost code reuse and maintainability in API automation by encapsulating common HTTP methods (GET, POST, PUT, DELETE, etc.) within a Pytest-friendly RequestUtil class, complete with logging, exception handling, session management, dynamic headers, and timeout support, plus practical test examples.
Why Wrap Request Methods
In API automation testing, repeatedly writing raw requests calls leads to duplicated code and makes maintenance difficult. Encapsulating HTTP verbs in a utility class hides request details, reduces repetition, improves readability, and centralizes changes such as base URLs or headers.
Setup and Basic Wrapper
1. Install the required library pip install requests 2. Create request_util.py and define the RequestUtil class
import requests
import json
class RequestUtil:
def __init__(self, base_url):
self.base_url = base_url
def get(self, endpoint, params=None, headers=None):
"""Send a GET request"""
url = f"{self.base_url}{endpoint}"
response = requests.get(url, params=params, headers=headers)
return response
def post(self, endpoint, data=None, json=None, headers=None):
"""Send a POST request"""
url = f"{self.base_url}{endpoint}"
response = requests.post(url, data=data, json=json, headers=headers)
return response
def put(self, endpoint, data=None, json=None, headers=None):
"""Send a PUT request"""
url = f"{self.base_url}{endpoint}"
response = requests.put(url, data=data, json=json, headers=headers)
return response
def delete(self, endpoint, headers=None):
"""Send a DELETE request"""
url = f"{self.base_url}{endpoint}"
response = requests.delete(url, headers=headers)
return response
def patch(self, endpoint, data=None, json=None, headers=None):
"""Send a PATCH request"""
url = f"{self.base_url}{endpoint}"
response = requests.patch(url, data=data, json=json, headers=headers)
return response
def options(self, endpoint, headers=None):
"""Send an OPTIONS request"""
url = f"{self.base_url}{endpoint}"
response = requests.options(url, headers=headers)
return response
def head(self, endpoint, headers=None):
"""Send a HEAD request"""
url = f"{self.base_url}{endpoint}"
response = requests.head(url, headers=headers)
return responseUsing the Wrapped Class in Pytest
Define a fixture that creates a RequestUtil instance and write test functions for each HTTP verb.
import pytest
from request_util import RequestUtil
@pytest.fixture
def api():
return RequestUtil("https://api.example.com")
def test_get_user_info(api):
response = api.get("/users/123")
assert response.status_code == 200
assert response.json()["name"] == "John Doe"
def test_create_user(api):
user_data = {"name": "John Doe", "email": "[email protected]"}
response = api.post("/users", json=user_data)
assert response.status_code == 201
assert response.json()["name"] == "John Doe"
def test_update_user(api):
user_data = {"name": "John Doe Updated", "email": "[email protected]"}
response = api.put("/users/123", json=user_data)
assert response.status_code == 200
assert response.json()["name"] == "John Doe Updated"
def test_delete_user(api):
response = api.delete("/users/123")
assert response.status_code == 204
def test_partial_update_user(api):
user_data = {"email": "[email protected]"}
response = api.patch("/users/123", json=user_data)
assert response.status_code == 200
assert response.json()["email"] == "[email protected]"
def test_options_user(api):
response = api.options("/users/123")
assert response.status_code == 200
assert "Allow" in response.headers
def test_head_user(api):
response = api.head("/users/123")
assert response.status_code == 200
assert response.headers["Content-Type"] == "application/json"Advanced Techniques
Logging
import logging
class RequestUtil:
def __init__(self, base_url):
self.base_url = base_url
self.logger = logging.getLogger(__name__)
def get(self, endpoint, params=None, headers=None):
url = f"{self.base_url}{endpoint}"
self.logger.info(f"Sending GET request to {url}")
response = requests.get(url, params=params, headers=headers)
self.logger.info(f"Response status code: {response.status_code}")
self.logger.info(f"Response content: {response.text}")
return response
# Other methods follow the same patternException Handling
import logging
import requests
class RequestUtil:
def __init__(self, base_url):
self.base_url = base_url
self.logger = logging.getLogger(__name__)
def get(self, endpoint, params=None, headers=None):
url = f"{self.base_url}{endpoint}"
try:
self.logger.info(f"Sending GET request to {url}")
response = requests.get(url, params=params, headers=headers)
response.raise_for_status()
self.logger.info(f"Response status code: {response.status_code}")
self.logger.info(f"Response content: {response.text}")
return response
except requests.exceptions.RequestException as e:
self.logger.error(f"Request failed: {e}")
raiseSession Management
class RequestUtil:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
self.logger = logging.getLogger(__name__)
def get(self, endpoint, params=None, headers=None):
url = f"{self.base_url}{endpoint}"
self.logger.info(f"Sending GET request to {url}")
response = self.session.get(url, params=params, headers=headers)
self.logger.info(f"Response status code: {response.status_code}")
self.logger.info(f"Response content: {response.text}")
return response
# Other methods are analogousDynamic Header Setting
class RequestUtil:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
self.logger = logging.getLogger(__name__)
def set_headers(self, headers):
"""Dynamically update request headers"""
self.session.headers.update(headers)Timeout Support
class RequestUtil:
def __init__(self, base_url, timeout=10):
self.base_url = base_url
self.timeout = timeout
self.session = requests.Session()
self.logger = logging.getLogger(__name__)
def get(self, endpoint, params=None, headers=None):
url = f"{self.base_url}{endpoint}"
self.logger.info(f"Sending GET request to {url}")
response = self.session.get(url, params=params, headers=headers, timeout=self.timeout)
self.logger.info(f"Response status code: {response.status_code}")
self.logger.info(f"Response content: {response.text}")
return response
# Other methods follow the same patternConclusion
Encapsulating HTTP request methods into a reusable RequestUtil class dramatically improves the maintainability and readability of API automation tests. By adding logging, exception handling, session reuse, dynamic headers, and configurable timeouts, the wrapper becomes a robust foundation for scalable test suites.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
