Building an API Automation Framework with Excel and Requests

Framework Overview

Managing API test cases across multiple endpoints often leads to scattered Python files that become difficult to maintain. When an API changes, developers must locate and update corresponding test files individually. This framework addresses the problem by centralizing test data in Excel files, allowing test case updates without code modifications.

Each API gets its own Excel worbkook containing two sheets:

  • Sheet 1: Interface metadata including endpoint name, URL path, and HTTP method
  • Sheet 2: Test cases where columns represent parameters and rows represent individual test scenarios, with the second-to-last column for expected results and the last column for test descriptions

Project Structure

project/
├── common/
│   └── logger.py           # Logging utilities
├── config/
│   ├── cfg.ini             # Configuration settings
│   └── readConfig.py       # Configuration reader
├── data/
│   ├── api_user.xlsx       # User API test cases
│   └── api_order.xlsx      # Order API test cases
├── logs/
│   └── test_execution.log
├── report/
│   └── test_report.html
└── run_main.py             # Entry point

Configuration Management

cfg.ini stores enviroment-specific settings:

[email]
smtp_server = smtp.example.com
port = 465
sender = sender@example.com
psw = authorization_code
receiver = recipient@example.com

[interface]
ip = 192.168.1.100
port = 8080

readConfig.py parses the configuration:

import os
import configparser

current_dir = os.path.dirname(os.path.realpath(__file__))
config_path = os.path.join(current_dir, "cfg.ini")
config = configparser.ConfigParser()
config.read(config_path, encoding="utf-8")

smtp_server = config.get("email", "smtp_server")
sender = config.get("email", "sender")
psw = config.get("email", "psw")
receiver = config.get("email", "receiver")
mail_port = config.get("email", "port")
host_ip = config.get("interface", "ip")
host_port = config.get("interface", "port")

Core Test Executor

api_test.py handles test data loading, API execution, and result validation:

import os
import xlrd
import requests
from datetime import datetime
from xlrd import xldate_as_tuple
from config import readConfig
from common.logger import Log

base_dir = os.path.dirname(__file__)
parent_dir = os.path.dirname(base_dir)
data_dir = os.path.join(parent_dir, 'data')
data_files = os.listdir(data_dir)

log = Log()
session = requests.session()

def execute_api_tests():
    for filename in data_files:
        workbook = xlrd.open_workbook(os.path.join(data_dir, filename))
        
        # Extract interface metadata from first sheet
        meta_sheet = workbook.sheet_by_index(0)
        api_name = meta_sheet.row_values(1)[0]
        api_path = meta_sheet.row_values(1)[1]
        http_method = meta_sheet.row_values(1)[2]
        
        # Load test cases from second sheet
        case_sheet = workbook.sheet_by_index(1)
        total_rows = case_sheet.nrows
        field_names = case_sheet.row_values(0)
        
        for row_idx in range(1, total_rows):
            request_params = {}
            
            # Build request parameters
            for col_idx in range(len(field_names) - 2):
                cell_type = case_sheet.cell(row_idx, col_idx).ctype
                cell_value = case_sheet.cell_value(row_idx, col_idx)
                
                if cell_type == 2 and cell_value % 1 == 0:
                    cell_value = int(cell_value)
                elif cell_type == 3:
                    date_obj = datetime(*xldate_as_tuple(cell_value, 0))
                    cell_value = date_obj.strftime('%Y/%m/%d')
                elif cell_type == 4:
                    cell_value = True if cell_value == 1 else False
                
                request_params[field_names[col_idx]] = cell_value
            
            # Extract expected results and descriptions
            expected_result = case_sheet.cell_value(row_idx, len(field_names) - 2)
            test_description = case_sheet.cell_value(row_idx, len(field_names) - 1)
            
            # Construct full URL
            target_url = f"http://{readConfig.host_ip}:{readConfig.host_port}{api_path}"
            request_headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                "X-Requested-With": "XMLHttpRequest",
                "Connection": "keep-alive"
            }
            
            # Execute request based on HTTP method
            if http_method == 'GET':
                response = session.get(target_url, params=request_params)
            elif http_method == 'POST':
                response = session.post(target_url, data=request_params, headers=request_headers)
            
            result = response.json()
            result_str = str(result).replace('None', 'null')
            
            log.info(f"API: {api_name}, Result: {result_str}")
            
            if result_str == str(expected_result).replace('None', 'null'):
                log.info("PASS")
            else:
                log.info("FAIL")

execute_api_tests()

Session Management for Authentication

Many APIs require authentication. The login handler establishes a session that persists across subsequent requests:

user_login.py:

import requests
from common.logger import Log

class AuthenticationHandler:
    def __init__(self, session):
        self.session = session
        self.logger = Log()
    
    def authenticate(self, username, password):
        login_url = "http://192.168.1.100:8080/backend/system/user/login"
        headers = {
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36",
            "X-Requested-With": "XMLHttpRequest"
        }
        payload = {"code": username, "passwd": password}
        
        response = self.session.post(login_url, headers=headers, data=payload)
        self.logger.info(f"Login response: {response.text}")
        return response.json()

Integration with test executor:

from case.user_login import AuthenticationHandler

def execute_api_tests():
    test_counter = 1
    auth = AuthenticationHandler(session)
    
    for filename in data_files:
        workbook = xlrd.open_workbook(os.path.join(data_dir, filename))
        # ... loading code ...
        
        for row_idx in range(1, total_rows):
            # Authenticate before each request
            auth.authenticate("test_user", "test_pass123")
            
            # Execute and validate
            if http_method == 'GET':
                response = session.get(target_url, params=request_params)
            elif http_method == 'POST':
                response = session.post(target_url, data=request_params, headers=request_headers)
            
            result = response.json()
            log.info(f"Test #{test_counter}: {api_name}, Description: {test_description}")
            
            test_counter += 1

Writing Results Back to Excel

Test results can be written directly into the Excel file using openpyxl, with color-coded formatting:

import openpyxl
from openpyxl.styles import Font

def execute_api_tests():
    test_counter = 1
    
    for filename in data_files:
        workbook = xlrd.open_workbook(os.path.join(data_dir, filename))
        
        # Open workbook for writing with openpyxl
        write_wb = openpyxl.load_workbook(os.path.join(data_dir, filename))
        write_ws = write_wb.worksheets[1]
        
        green_font = Font(color="37b400")
        red_font = Font(color="ff0000")
        
        # ... loading code ...
        
        for row_idx in range(1, total_rows):
            # ... parameter extraction ...
            
            # Execute request
            if http_method == 'GET':
                response = session.get(target_url, params=request_params)
            elif http_method == 'POST':
                response = session.post(target_url, data=request_params, headers=request_headers)
            
            result = response.json()
            result_str = str(result).replace('None', 'null')
            
            # Write result with color coding
            if result_str == str(expected_result).replace('None', 'null'):
                write_ws.cell(row=row_idx + 1, column=ncols, value='PASS')
                write_ws.cell(row=row_idx + 1, column=ncols).font = green_font
            else:
                write_ws.cell(row=row_idx + 1, column=ncols, value='FAIL')
                write_ws.cell(row=row_idx + 1, column=ncols).font = red_font
            
            write_wb.save(os.path.join(data_dir, filename))
            test_counter += 1
    
    log.info(f"Total tests executed: {test_counter}")

Excel Structure

Sheet 1 (Interface Info):

Column A Column B Column C
接口名称 接口地址 请求方式
UserLogin /api/user/login POST

Sheet 2 (Test Cases):

code passwd expected_result description result
admin admin123 {"code":200,"msg":"success"} Valid credentials PASS
admin wrong {"code":401,"msg":"unauthorized"} Invalid password PASS

This framework enables test maintainance through Excel editing alone, eliminating the need to modify Python code when API parameters or expectations change.

Tags: API Testing Automation Framework Excel Requests python

Posted on Tue, 19 May 2026 11:53:19 +0000 by phphelpme