How to Wrap HTTP Request Methods in Pytest for Cleaner API Tests

This guide explains why encapsulating HTTP request methods in Pytest improves code reuse and maintainability, and provides complete Python examples for GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, and custom requests, plus advanced tips on logging, exception handling, and session management.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
How to Wrap HTTP Request Methods in Pytest for Cleaner API Tests

Why encapsulate request methods

In API automation testing, repeatedly calling requests directly leads to duplicated code and fragile test cases. Wrapping common HTTP verbs in a utility class reduces repetition, centralises URL and header changes, and makes test scripts easier to read and maintain.

Basic request wrappers

GET

import requests

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url

    def get(self, endpoint, params=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        response = requests.get(url, params=params, headers=headers)
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.get("/users", params={"page": 1, "limit": 10})
    print(response.status_code)
    print(response.json())

POST

import requests

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url

    def post(self, endpoint, data=None, json=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        response = requests.post(url, data=data, json=json, headers=headers)
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.post(
        "/users",
        json={"name": "John Doe", "email": "[email protected]"},
        headers={"Content-Type": "application/json"}
    )
    print(response.status_code)
    print(response.json())

PUT

import requests

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url

    def put(self, endpoint, data=None, json=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        response = requests.put(url, data=data, json=json, headers=headers)
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.put(
        "/users/123",
        json={"name": "John Doe Updated", "email": "[email protected]"},
        headers={"Content-Type": "application/json"}
    )
    print(response.status_code)
    print(response.json())

DELETE

import requests

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url

    def delete(self, endpoint, headers=None):
        url = f"{self.base_url}{endpoint}"
        response = requests.delete(url, headers=headers)
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.delete("/users/123", headers={"Content-Type": "application/json"})
    print(response.status_code)
    print(response.json())

PATCH

import requests

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url

    def patch(self, endpoint, data=None, json=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        response = requests.patch(url, data=data, json=json, headers=headers)
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.patch(
        "/users/123",
        json={"email": "[email protected]"},
        headers={"Content-Type": "application/json"}
    )
    print(response.status_code)
    print(response.json())

OPTIONS

import requests

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url

    def options(self, endpoint, headers=None):
        url = f"{self.base_url}{endpoint}"
        response = requests.options(url, headers=headers)
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.options("/users", headers={"Content-Type": "application/json"})
    print(response.status_code)
    print(response.headers)

HEAD

import requests

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url

    def head(self, endpoint, headers=None):
        url = f"{self.base_url}{endpoint}"
        response = requests.head(url, headers=headers)
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.head("/users", headers={"Content-Type": "application/json"})
    print(response.status_code)
    print(response.headers)

Custom request

import requests

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url

    def custom_request(self, method, endpoint, data=None, json=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        response = requests.request(method, url, data=data, json=json, headers=headers)
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.custom_request(
        "CUSTOM_METHOD",
        "/users",
        json={"name": "John Doe", "email": "[email protected]"},
        headers={"Content-Type": "application/json"}
    )
    print(response.status_code)
    print(response.json())

Advanced techniques

Adding logging

import requests
import logging

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url
        logging.basicConfig(level=logging.INFO)

    def get(self, endpoint, params=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        logging.info(f"Sending GET request to {url}")
        response = requests.get(url, params=params, headers=headers)
        logging.info(f"Response status code: {response.status_code}")
        logging.info(f"Response content: {response.text}")
        return response

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    response = request_util.get("/users", params={"page": 1, "limit": 10})
    print(response.status_code)
    print(response.json())

Exception handling

import requests
import logging

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url
        logging.basicConfig(level=logging.INFO)

    def get(self, endpoint, params=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        try:
            logging.info(f"Sending GET request to {url}")
            response = requests.get(url, params=params, headers=headers)
            response.raise_for_status()
            logging.info(f"Response status code: {response.status_code}")
            logging.info(f"Response content: {response.text}")
            return response
        except requests.exceptions.RequestException as e:
            logging.error(f"Request failed: {e}")
            raise

# Example usage
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    try:
        response = request_util.get("/users", params={"page": 1, "limit": 10})
        print(response.status_code)
        print(response.json())
    except Exception as e:
        print(f"Error: {e}")

Session management

import requests
import logging

class RequestUtil:
    def __init__(self, base_url):
        self.base_url = base_url
        self.session = requests.Session()
        logging.basicConfig(level=logging.INFO)

    def get(self, endpoint, params=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        logging.info(f"Sending GET request to {url}")
        response = self.session.get(url, params=params, headers=headers)
        logging.info(f"Response status code: {response.status_code}")
        logging.info(f"Response content: {response.text}")
        return response

    def post(self, endpoint, data=None, json=None, headers=None):
        url = f"{self.base_url}{endpoint}"
        logging.info(f"Sending POST request to {url}")
        response = self.session.post(url, data=data, json=json, headers=headers)
        logging.info(f"Response status code: {response.status_code}")
        logging.info(f"Response content: {response.text}")
        return response

# Example usage (login then reuse token)
if __name__ == "__main__":
    request_util = RequestUtil("https://api.example.com")
    login_resp = request_util.post(
        "/login",
        json={"username": "testuser", "password": "testpass"}
    )
    token = login_resp.json()["token"]
    request_util.session.headers.update({"Authorization": f"Bearer {token}"})
    users_resp = request_util.get("/users")
    print(users_resp.status_code)
    print(users_resp.json())

Conclusion

By encapsulating HTTP request methods in a reusable RequestUtil class, API test code becomes more concise, maintainable, and robust. The guide covers wrappers for all common verbs, demonstrates logging, error handling, and session reuse, providing a solid foundation for efficient automated testing.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Pythonautomationloggingcode-reuseAPI testingHTTP requestspytest
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.