基于租车管理系统的自动化接口测试的分层设计

一、自动化接口测试流程详解

分层设计

在这里插入图片描述
各模块之间的逻辑关系
在这里插入图片描述

1. 数据准备阶段:读测试剧本
  • 做什么:就像拍电影需要剧本,测试也需要提前写好的“测试剧本”(YAML文件)。

  • 怎么做

    • TestApi类YamlTools 工具读取 car_login.yamladd_cars.yaml 文件。
class TestApi:
    # 读取YAML文件中的测试数据,并存储在类变量中
    car_login_data = YamlTools.read_yaml_file("api\\car_login.yaml")[0]  # 读取登录接口的测试数据
    add_cars_data = YamlTools.read_yaml_file("api\\add_cars.yaml")[0]  # 读取添加车辆接口的测试数据
    delete_cars_data = YamlTools.read_yaml_file("api\\delete_cars.yaml")[0]  # 读取删除车辆接口的测试数据
  • 这些文件里写了(.yaml):要测试哪些接口(登录,添加,删除)、请求的URL、参数、预期结果。

  • 登录所需的驱动数据

-
 request:
    epic: carRental项目
    feature: 租车系统
    story: 登录接口
    method: post
    url: /carRental/login/login.action
    headers: null
    cookies: null

  parametrize:
    - {title: 输入正确的用户名称和密码,登录成功,data: {loginname: "admin",pwd: "123456"},expect: {status_code: '0'},params: null,json: null,files: null}
  • 添加车辆所需的驱动数据
  -
      request:
        epic: carRental项目
        feature: 租车系统
        story: 添加车辆接口
        method: post
        url: /carRental/car/addCar.action
        headers: null
        cookies: null
    
    
      parametrize:
        - {title: 输入合法车辆信息,data: {carnumber: "010",cartype: null,color: null,carimg: {name: "mf",path: "C:\\car_pictures\\red_car.jpg"},description: null,price: "234",rentprice: 987,deposit: 9876,isrenting: 0},expect: {code: '0',msg: "添加成功"},params: null,json: {code: "carnumber"},files: null}
        - {title: 输入空车牌号添加失败,data: {carnumber: null,cartype: null,color: null,carimg: {name: "mf",path: "C:\\car_pictures\\red_car.jpg"},description: null,price: "234",rentprice: 987,deposit: 9876,isrenting: 0},expect: {code: '-1',msg: "添加失败"},params: null,json: {code: "carnumber"},files: null}
        - {title: 车辆信息全部为空,data: {carnumber: null,cartype: null,color: null,carimg: {name: "mf",path: "C:\\car_pictures\\red_car.jpg"},description: null,price: null,rentprice: null,deposit: null,isrenting: 0},expect: {code: '-1',msg: "添加失败"},params: null,json: {code: "carnumber"},files: null}
  • 删除车辆信息所需的驱动数据
-
  request:
    epic: carRental项目
    feature: 租车系统
    story: 删除车辆接口
    method: post
    url: /carRental/car/deleteBatchCar.action
    headers: null
    cookies: null

  parametrize:
    - {title: 输入正确车牌号,删除成功,data: {ids: '007'},expect: {code: 0,msg: "删除成功"},params: null,json: null,files: null}
    - {title: 输入错误车牌号,失败,data: {ids: '009'},expect: {code: -1,msg: "删除失败"},params: null,json: null,files: null}
2. 测试执行阶段:按剧本演戏
  • 第一步:登录系统

    • TestApi类调用 Event.car_login() 方法,发送账号密码到登录接口

    • def test_car_login(self, parametrize):
          """
          测试车辆登录接口
          :param parametrize: 参数化数据,包含请求参数和预期结果
          """
          # 调用Event类的car_login方法,发送登录请求
          `rp = Event.car_login(request=TestApi.car_login_data["request"], parametrize=parametrize)
      

      Event类中Event.car_login()方法

    • @classmethod
      @allure.step("登录系统")
      def car_login(self, request, parametrize):
          # 调用BaseRequest类的execute_api_request方法,执行API请求。
          # request参数包含API请求的详细信息,parametrize参数包含请求的参数。
          rp = BaseRequest.execute_api_request(request=request, parametrize=parametrize)
          # 返回API请求的响应结果。
          return rp
      
  • 第二步:上传车辆图片和添加车辆

  • 1.上传图片

    • TestApi类调用 Event.upload_car_picturce(),从本地选择图片( C:\car_pictures\red_car.jpg)。
@classmethod
@allure.step("上传图片")
def upload_car_picturce(self, request, parametrize):
    # 从parametrize参数中获取图片的文件路径和文件名。
    file_path = parametrize["data"]["carimg"]["path"]
    filename = parametrize["data"]["carimg"]["name"]
    
    # 以二进制读取模式打开图片文件。
    with open(file_path, 'rb') as f:
        # 构造文件字典,用于上传文件。
        files = {filename: (file_path, f)}
        # 将文件字典添加到parametrize参数中。
        parametrize["files"] = files
        # 保存原始的URL,以便后续恢复。
        url1 = request["url"]
        # 修改请求的URL为上传文件的API地址。
        request["url"] = "/carRental/file/uploadFile.action"
        # 调用BaseRequest类的execute_api_request方法,执行上传文件的API请求。
        rp = BaseRequest.execute_api_request(request=request, parametrize=parametrize)
        # 上传完成后,清空parametrize中的files字段。
        parametrize["files"] = None
        # 恢复原始的URL。
        request["url"] = url1
        # 关闭文件。
        f.close()
    # 返回API请求的JSON格式响应结果。
    return rp.json()
  • 程序自动把图片包装成HTTP请求,发送到服务器的“上传接口”。

    2.添加车辆

  • TestApi类调用 Event.add_cars(),把车牌号、价格、图片地址等信息发送到“添加车辆接口”。

  • @classmethod
    @allure.step("添加车辆")
    def add_cars(cls, request, parametrize, request2, parametrize2):
        # 调用car_login方法,执行登录操作。
        Event.car_login(request=request, parametrize=parametrize)
        # 调用upload_car_picturce方法,上传车辆图片,并获取响应结果。
        rp = Event.upload_car_picturce(request=request2, parametrize=parametrize2)
        # 从响应结果中获取图片的URL。
        data1 = rp["data"]
        # 将图片的URL添加到parametrize2参数中。
        parametrize2["data"]["carimg"] = data1["src"]
        # 调用BaseRequest类的execute_api_request方法,执行添加车辆的API请求。
        rp = BaseRequest.execute_api_request(request=request2, parametrize=parametrize2)
        # 返回API请求的响应结果。
        return rp
    

    正向用例结果

  • 程序会自动检查:是否用了刚上传的图片地址?参数是否合法?

  • 第三步:删除车辆

    • TestApi类调用 Event.delete_car(),发送要删除的车牌号到“删除接口”。

    • # 使用@classmethod装饰器定义类方法,表示该方法属于类而不是实例。
      # 使用@allure.step装饰器,表示在Allure报告中标记该步骤为“删除车辆信息”。
      @classmethod
      @allure.step("删除车辆信息")
      def delete_car(cls, request3, parametrize3):
          # 调用BaseRequest类的execute_api_request方法,执行删除车辆信息的API请求。
          rp = BaseRequest.execute_api_request(request=request3, parametrize=parametrize3)
          # 返回API请求的响应结果。
          return rp
      

      服务器会检查车牌是否存在,返回“删除成功”或“删除失败”。

3. 结果验证阶段:检查演得对不对
  • 检查点1:状态码是否为200(表示网络请求成功)。

  • 检查点2:返回的 msg 字段是否和预期一致(如“添加成功”)。

  • 例如:

    assert rp.json()["msg"] == "添加成功"  # 如果实际结果不符,测试失败
    
4. 报告生成阶段:制作成绩单
  • Allure 生成漂亮的测试报告:
    • 展示每个步骤的请求参数、服务器响应。

    • 用颜色标记成功(绿色)和失败(红色)的测试用例。

    • 报告中会显示“上传图片”步骤的URL、上传的文件名。

      源代码:

      import logging   # 日志包
      import logging.handlers
      import os.path
      import time
      from config.api.conf import LOG_DIR
      import colorlog   # 对日志信息进行颜色划分输出
      
      log_colors_config = {
          'DEBUG': 'white',
          'INFO': 'green',
          'WARNING': 'yellow',
          'ERROR': 'red',
          'CRITICAL': 'bold_red',
      }
      # 创建日志管理器
      log = logging.getLogger('log_name')
      # api_log = logging.getLogger('api')#
      # 准备时间字符串,用在log文件名
      #daytime = time.strftime('%Y-%m-%d_%H-%M-%S')
      daytime = time.strftime('%Y-%m-%d')
      
      # 项目根目录下 的 log 文件夹
      
      if not os.path.exists(LOG_DIR):
          os.makedirs(LOG_DIR)
          print("目录已经创建")
          # 目录不存在时创建 目录
      # 创建文件日志的路径文件名
      filename = LOG_DIR + f'/run_api_log_{daytime}.log'
          # 命令台 输出对象
      # 创建命令台处理器
      sh = logging.StreamHandler()
      # 创建文件处理器
      # when 是 日志分割标志
      # interval 一天生成一个
      # 日志的数量 控制 30
      fh = logging.handlers.TimedRotatingFileHandler(filename, when='midnight', interval=1, backupCount=15,encoding='utf-8')
      # 文件日志的格式
      fmt = "[%(levelname)s] [%(asctime)s.%(msecs)03d] : %(message)s : %(filename)s -> %(funcName)s line:%(lineno)d"
      # 命令台的日志格式
      smt = "[%(levelname)s] %(log_color)s [%(asctime)s.%(msecs)03d] : %(message)s : %(filename)s -> %(funcName)s line:%(lineno)d"
          # 日志级别设置, 命令台日志 和 文件日志级别,可以不同,进行单独设置,设置冲突时,谁高以谁为准
      
      # 利用日志格式,生成 文件 和日志格式处理器
      file_formatter = logging.Formatter(fmt, datefmt='%Y_%m_%d %H:%M:%S')
      console_formattre = colorlog.ColoredFormatter(smt,datefmt='%Y_%m_%d %H:%M:%S',log_colors=log_colors_config)
      
      sh.setFormatter(console_formattre)
      fh.setFormatter(file_formatter)
      
      log.setLevel(logging.DEBUG)
      fh.setLevel(logging.DEBUG)
      sh.setLevel(logging.DEBUG)
      
5. 通知阶段:发邮件给审核,准备上映!!!
  • send_email_with_attachment() 函数:

    • 把测试报告(HTML文件)作为附件。

    • 自动登录QQ邮箱,发送给指定收件人(如项目经理)。

    • 邮件正文会写:“测试已完成,请查收附件”。

    • 源代码

    • from email.mime.multipart import MIMEMultipart
      from email.mime.text import MIMEText
      import smtplib
      import os
      from data.api.env import MAIL_ENV
      
      def send_email_with_attachment(sender_email=MAIL_ENV["发件人邮箱"], auth_code=MAIL_ENV["授权码"], receiver_emails=MAIL_ENV["收件人列表"], subject=MAIL_ENV["邮件标题"], body_html=MAIL_ENV["邮件正文"], attachment_path=MAIL_ENV["附件目录"]):
          # 创建邮件
          mail = MIMEMultipart()
          mail['From'] = sender_email
          mail['To'] = ', '.join(receiver_emails)  # 这只是为了显示,实际发送时不需要
          mail['Subject'] = subject
          # 添加附件
          if os.path.exists(attachment_path):
              with open(attachment_path, 'r', encoding='utf-8') as file:
                  attachment_content = file.read()
              attachment = MIMEText(attachment_content, 'html', 'utf-8')  # 如果附件是 HTML,使用 'html'
              attachment['Content-Disposition'] = f'attachment; filename="{os.path.basename(attachment_path)}"'
              mail.attach(attachment)
          else:
              print(f"警告: 附件路径 {attachment_path} 不存在")
          # 添加正文
          body = MIMEText(body_html, "html", "utf-8")
          mail.attach(body)
          # 邮箱设置
          host = 'smtp.qq.com'
          port = 465  # 使用 SSL 连接
          # 发送邮件
          try:
              smtp = smtplib.SMTP_SSL(host, port)  # 使用 SMTP_SSL 类进行 SSL 连接
              smtp.login(sender_email, auth_code)
              smtp.sendmail(sender_email, receiver_emails, mail.as_string())
              smtp.quit()  # 使用 quit() 退出连接
              print("邮件发送完毕")
          except Exception as e:
              print(f"邮件发送失败: {e}")
      

二、代码模块逻辑关系详解

  • TestApi类:导演,负责指挥整个测试流程。
    • 告诉 Event 类:“先登录,再上传图片,最后添加车辆”。
    • 通过 @pytest.mark.parametrize 读取YAML中的不同测试场景(如正确密码、错误密码)。
  • Event类:演员,执行具体的操作。
    • 每个方法(如 car_login)对应一个业务步骤。
    • 依赖 BaseRequest 发送HTTP请求,就像演员需要道具组准备道具。
  • BaseRequest类:道具组,负责处理HTTP请求细节。
    • 维护一个 Session 对象,自动保存Cookies(如登录后的身份信息)。
    • 把请求参数、URL拼接完整,并记录到日志和Allure报告中。
  • YamlTools类:剧本管理员,负责读取和解析YAML文件。
    • 将YAML中的测试数据转换成Python字典,供 TestApi 使用。
  • 日志模块:场记,记录每一步发生了什么。
    • 控制台显示彩色日志(绿色是成功,红色是错误)。
    • 每天自动生成一个日志文件,最多保留15天。
  • 邮件模块:播报员,测试结束后发通知。
    • 把Allure生成的报告打包成邮件附件,通过QQ邮箱发送。

三、核心代码模块功能说明

1. TestApi类:测试用例组织
  • 功能:定义具体的测试用例,如“测试登录接口”。

  • 关键技术

    • 参数化测试:用 @pytest.mark.parametrize 实现多场景测试。

      # 
      @pytest.mark.parametrize("parametrize", car_login_data["parametrize"])
      def test_car_login(self, parametrize):
          # 调用Event.car_login()发送请求
          # 断言检查结果
      
    • Allure报告标记:用 @allure.epic@allure.feature 分层展示测试报告。

2. BaseRequest类:HTTP请求处理
  • 功能:发送HTTP请求,处理响应,记录日志。

  • 关键技术

    • Session保持:自动携带Cookies,实现登录状态维持。

      sess = requests.session()  # 创建一个会“记住”Cookies的会话
      rp = sess.post(url, data=data)  # 第二次请求自动带上登录后的Cookies
      
    • Allure附件:把请求参数添加到测试报告中。

      allure.attach(url, "请求地址")  # 在报告中显示“url地址:http://http://localhost:63342/test_car/reports/”
      
3. Event类:业务流程封装
  • 功能:串联多个接口,实现完整业务流。

  • 关键技术

    • 步骤串联:添加车辆前必须先登录和上传图片。

      def add_cars(...):
          Event.car_login(...)  # 先登录      可以省略
          rp = Event.upload_car_picturce(...)  # 再上传图片
          # 最后添加车辆
      
    • 文件上传处理:自动读取本地图片并包装成HTTP文件格式。

      with open(file_path, 'rb') as f:
          files = {filename: (file_path, f)}  # 构造文件字典
          parametrize["files"] = files  # 添加到请求参数
      
4. YamlTools类:数据驱动管理
  • 功能:从YAML文件读取测试数据,实现数据和代码分离。

  • 关键技术

    • 动态路径解析:自动拼接项目路径,避免硬编码。

      path = YamlTools.get_project_path() + "testdata\\" + path
      
    • YAML解析:将YAML内容转换为Python字典。

      data = yaml.load(file_content, Loader=yaml.FullLoader)
      
5. 日志模块:执行过程跟踪
  • 功能:记录测试过程中的详细信息,便于排查问题。

  • 关键技术

    • 日志轮转:每天生成一个新日志文件,最多保留15天。

      fh = logging.handlers.TimedRotatingFileHandler(filename, when='midnight', backupCount=15)
      
    • 彩色输出:不同级别的日志显示不同颜色(如错误是红色)。

console_formatter = colorlog.ColoredFormatter(..., log_colors=log_colors_config)
6. 邮件模块:结果通知
  • 功能:测试完成后自动发送邮件报告。

  • 关键技术

  • MIME协议:构建带附件的邮件。

mail = MIMEMultipart()
mail.attach(MIMEText(attachment_content, 'html', 'utf-8'))

SSL加密传输:通过QQ邮箱的465端口安全发送邮件。

smtp = smtplib.SMTP_SSL('smtp.qq.com', 465)

四、关键代码段说明

1. 参数化测试用例设计

@pytest.mark.parametrize("parametrize", car_login_data["parametrize"])
def test_car_login(self, parametrize):
    # 动态加载YAML中的测试场景
    rp = Event.car_login(...)
    assert rp.status_code == 200

2. 文件上传处理逻辑

with open(file_path, 'rb') as f:
    files = {filename: (file_path, f)}  # 符合requests的文件格式
    # 临时修改URL实现接口切换

3. 日志模块配置

fh = logging.handlers.TimedRotatingFileHandler(
    filename, when='midnight', backupCount=15)  # 自动日志轮转

4. 邮件内容构建

mail.attach(MIMEText(attachment_content, 'html', 'utf-8'))  # 支持HTML附件

五、执行效果预览
  1. Allure报告展示
  • 分层展示Epic/Feature/Story
    • 每个步骤的请求/响应详情
    • 失败用例的错误上下文
  1. 邮件报告效果
    • 带HTML附件的专业测试报告
      with open(file_path, ‘rb’) as f:
      files = {filename: (file_path, f)} # 符合requests的文件格式

    临时修改URL实现接口切换


**3. 日志模块配置**

```python
fh = logging.handlers.TimedRotatingFileHandler(
    filename, when='midnight', backupCount=15)  # 自动日志轮转

4. 邮件内容构建

mail.attach(MIMEText(attachment_content, 'html', 'utf-8'))  # 支持HTML附件

详细源代码以及租车管理系统如何配置可以私聊我,私发哈,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值