基于 Python 自动化接口测试(踩坑与实践)

文档:基于 Python 的自动化接口测试


目录

  1. 背景
  2. 问题描述与解决思路
  3. 核心代码
  4. 修改点及其详细解释
  5. 最终测试结果
  6. 后续优化建议

1. 问题背景

本项目旨在使用 Python 模拟浏览器的请求行为,测试文章分页接口的可用性。测试目标接口如下:

复制代码
bash

coderboots
http://localhost:8081/api/article/page

接口需要携带与浏览器完全一致的请求头和 Cookies,同时需支持分页参数(如 currentsize)。

重点是, 使用postman和浏览器都可以正常测试,但是使用python脚本测试失败。 ---遇到网络问题, 一定要记得思考是否是代理问题。


2. 问题描述与解决思路

问题描述

  1. 初次尝试时,脚本请求失败,返回 502 Bad Gateway 错误。
  2. 原因分析表明,requests 库默认继承系统代理配置,而代理拦截或错误转发了请求。
  3. 此外,初始代码未完全复制浏览器的 Headers 和 Cookies。

解决思路

  1. 禁用代理 :显式设置 proxiesNone,避免系统代理干扰。
  2. 完整复制 Headers 和 Cookies:确保请求与浏览器的行为一致。
  3. 日志改进:详细记录请求 URL、Headers 和响应信息,便于调试和问题定位。
  4. 重试机制:为网络不稳定的情况添加重试逻辑,提高脚本健壮性。

3. 核心代码

以下是经过优化的测试脚本:

复制代码
python


coderboots
import requests
import logging
import time

class ArticleApiTest:
    def __init__(self):
        self.base_url = "http://localhost:8081/api/article"
        
        # 设置请求头,确保与浏览器一致(后面证明只需要修改代理即可)
        self.headers = {

        }
        
        # 设置Cookies
        self.cookies = {
          
        }
        
        # 创建 session 并禁用代理
        self.session = requests.Session()
        self.session.headers.update(self.headers)
        self.session.cookies.update(self.cookies)
        self.session.proxies = {'http': None, 'https': None}
        
        # 设置超时时间
        self.timeout = 10
        
        # 设置日志记录
        logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
        self.logger = logging.getLogger(__name__)

    def get_page(self, current=1, size=10):
        """发送分页请求"""
        url = f"{self.base_url}/page"
        params = {'current': current, 'size': size}
        
        try:
            # 发送请求
            response = self.session.get(url, params=params, timeout=self.timeout, verify=False)
            
            # 记录日志
            self.logger.info(f"Request URL: {response.url}")
            self.logger.info(f"Response Status: {response.status_code}")
            
            if response.status_code == 200:
                self.logger.info("Request successful")
                self.logger.info(f"Response Data: {response.json()}")
            else:
                self.logger.error(f"Request failed with status code: {response.status_code}")
            
            return response
        except requests.RequestException as e:
            self.logger.error(f"Request failed: {str(e)}")
            return None

def main():
    tester = ArticleApiTest()
    print("开始测试...")
    response = tester.get_page()
    if response and response.status_code == 200:
        print("✅ 测试成功")
    else:
        print("❌ 测试失败")

if __name__ == "__main__":
    main()

4. 修改点及详细解释

修改点 1:禁用代理 --重点和关键

  • 原因 :初次运行时,系统代理干扰了请求,导致 502 Bad Gateway 错误。
  • 解决方法 :在 requests.Session 中添加 proxies 参数,将 httphttps 显式设置为 None

代码:

复制代码
python


coderboots
self.session.proxies = {'http': None, 'https': None}

修改点 2:完整的 Headers 和 Cookies

  • 原因 :部分请求头(如 User-Agentsec-ch-ua)以及 Cookies 在初始代码中未设置,导致服务器未正确识别请求。(由于本次后端其实没有做特别的鉴权,所以这里其实设置为空也可以正常访问)
  • 解决方法 :复制浏览器中的完整 Headers 和 Cookies 并在 requests.Session 中更新。

代码:

复制代码
python


coderboots
self.headers = {
    # 浏览器请求头
}
self.cookies = {
    # 浏览器 Cookies
}

修改点 3:日志记录

  • 原因:初始代码缺乏详细的日志,不利于调试。
  • 解决方法:添加请求 URL、Headers 和响应状态的详细日志记录。

代码:

复制代码
python


coderboots
self.logger.info(f"Request URL: {response.url}")
self.logger.info(f"Response Status: {response.status_code}")
self.logger.info(f"Response Data: {response.json()}")

5. 最终测试结果

运行脚本后,成功返回分页数据,日志记录如下:

复制代码
yaml


coderboots
2025-01-08 01:42:21,175 - INFO - Request URL: http://localhost:8081/api/article/page?current=1&size=10
2025-01-08 01:42:21,175 - INFO - Response Status: 200
2025-01-08 01:42:21,176 - INFO - Request successful
2025-01-08 01:42:21,176 - INFO - Response Data: {...}
✅ 测试成功

6. 后续优化建议(略)

  1. 动态 CSRF Token 支持
    • 如果接口需要动态 Token,可以在发送请求前自动提取并添加到 Headers。
  2. 重试机制
    • 针对请求失败的情况(如网络不稳定或服务器错误),增加智能重试机制。
  3. 异步请求
    • 如果需要测试多个接口,可以使用 asyncio 实现异步请求,提高效率。
  4. 自动化集成
    • 将脚本集成到 CI/CD 管道中,定期验证接口的可用性。

通过上述改进,该脚本现已具备稳定性、可调试性和一致性,能够准确模拟浏览器请求行为并测试目标接口。

最后给一个好用的模版:

修改template 为你的模块名称即可

复制代码
import requests
import json
from colorama import init, Fore, Style
import os

# 初始化colorama
init()

# 禁用系统代理
os.environ['no_proxy'] = '*'


class TemplateApiTest:
    def __init__(self, base_url="http://localhost:8081/api"):
        self.base_url = base_url
        self.headers = {
            "Content-Type": "application/json"
        }

    def print_response(self, api_name, response):
        """格式化打印响应结果"""
        print(f"\n{Fore.CYAN}测试接口:{Style.RESET_ALL} {api_name}")
        print(f"{Fore.CYAN}请求URL:{Style.RESET_ALL} {response.url}")
        print(f"{Fore.CYAN}状态码:{Style.RESET_ALL} {response.status_code}")

        if response.status_code == 200:
            print(f"{Fore.GREEN}响应结果:{Style.RESET_ALL}")
            try:
                formatted_json = json.dumps(response.json(), ensure_ascii=False, indent=2)
                print(formatted_json)
            except:
                print(response.text)
        else:
            print(f"{Fore.RED}错误响应:{Style.RESET_ALL}")
            print(response.text)
        print("-" * 80)

    def test_get_page(self):
        """测试分页查询模板"""
        params = {
            "current": 1,
            "size": 10,
            "category": "通用模板"
        }
        response = requests.get(
            f"{self.base_url}/template/page",
            params=params,
            headers=self.headers
        )
        self.print_response("分页查询模板", response)

    def test_get_template_by_id(self):
        """测试根据ID获取模板"""
        template_id = 1
        response = requests.get(
            f"{self.base_url}/template/{template_id}",
            headers=self.headers
        )
        self.print_response(f"获取模板(ID: {template_id})", response)

    def test_save_template(self):
        """测试保存新模板"""
        template_data = {
            "name": "测试模板",
            "content": "这是一个测试模板的内容",
            "category": "通用模板",
            "description": "用于测试的模板"
        }
        response = requests.post(
            f"{self.base_url}/template",
            headers=self.headers,
            data=json.dumps(template_data)
        )
        self.print_response("创建新模板", response)

    def test_update_template(self):
        """测试更新模板"""
        template_data = {
            "id": 1,
            "name": "更新后的测试模板",
            "content": "这是更新后的测试模板内容",
            "category": "通用模板",
            "description": "已更新的测试模板"
        }
        response = requests.put(
            f"{self.base_url}/template",
            headers=self.headers,
            data=json.dumps(template_data)
        )
        self.print_response("更新模板", response)

    def test_delete_template(self):
        """测试删除模板"""
        template_id = 1
        response = requests.delete(
            f"{self.base_url}/template/{template_id}",
            headers=self.headers
        )
        self.print_response(f"删除模板(ID: {template_id})", response)


def main():
    # 创建测试实例
    tester = TemplateApiTest()

    try:
        print(f"\n{Fore.YELLOW}=== 开始测试模板接口 ==={Style.RESET_ALL}")
        tester.test_get_page()
        tester.test_get_template_by_id()
        tester.test_save_template()
        tester.test_update_template()
        tester.test_delete_template()
        print(f"{Fore.YELLOW}=== 模板接口测试完成 ==={Style.RESET_ALL}\n")
    except requests.exceptions.RequestException as e:
        print(f"{Fore.RED}测试过程中发生错误: {e}{Style.RESET_ALL}")


if __name__ == "__main__":
    main()
相关推荐
weixin_466485111 分钟前
PyCharm中运行.py脚本程序
ide·python·pycharm
Jay_2731 分钟前
python项目如何创建docker环境
开发语言·python·docker
老胖闲聊1 小时前
Python Django完整教程与代码示例
数据库·python·django
爬虫程序猿1 小时前
利用 Python 爬虫获取淘宝商品详情
开发语言·爬虫·python
noravinsc1 小时前
django paramiko 跳转登录
后端·python·django
声声codeGrandMaster1 小时前
Django之表格上传
后端·python·django
元直数字电路验证1 小时前
Python数据分析及可视化中常用的6个库及函数(一)
python·numpy
waterHBO1 小时前
一个小小的 flask app, 几个小工具,拼凑一下
javascript·vscode·python·flask·web app·agent mode·vibe coding
智商不够_熬夜来凑1 小时前
anaconda安装playwright
开发语言·python
溜溜刘@♞1 小时前
python变量
python