基于Python 简易实现接口测试自动化

news2024/9/19 16:02:02

目录

实现思路

统筹脚本

请求封装 

日志封装

结果比对

结果邮件

用例获取及数据格式化

请求url转换

测试用例excel结构

测试报告

邮件接收结果

资料获取方法


实现思路

使用excel管理用例用例信息,requests模块发送http请求,实现了记录日志,邮件发送测试报告的功能

目录结构如下:

下面直接上代码:

统筹脚本

请求封装 

# coding:utf-8
import json
import requests
 
from logging_save import logger
from result_check import Result_check
from url_transform import urltransform
 
 
class Interface:
    def __init__(self, ):
        pass
 
    def interfaceTest(self, case_list):
        """
        接口调用主函数
        """
        # 用于存结果
        res_flags = []
        # 用于存请求报文
        request_urls = []
        # 用于存返回报文
        responses = []
        # 用户存失败的用例
        failed_case = []
        # 统计成功失败的用例数
        count_success = 0
        count_failure = 0
        for case in case_list:
            try:
                # 模块
                product = case[0]
                # 用例id
                case_id = case[1]
                # 用例标题
                interface_name = case[2].strip('\n')
                # 用例描述
                case_detail = case[3]
                # 请求方式
                method = case[4]
                # 请求url
                url = case[5]
                # 入参
                param = case[6]
                # 预期结果
                res_check = case[7]
            except Exception as e:
                return '测试用例格式不正确!%s' % e
            # 定义消息头信息
            headers = {'content-type': 'application/json',
                       'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:22.0) Gecko/20100101 Firefox/22.0'}
            # 对url进行封装
            new_url = urltransform().urltransform(url, method, param)
            if method.upper() == 'GET':
                results = requests.get(new_url).text
                logger.info(u'正在调用接口: %s' % interface_name)
                # print results
                responses.append(results)
                # 用于存储预期结果与实际结果的比较结果
                res = Result_check().interface_result_check(results, res_check)
                request_urls.append(new_url)
            else:
                request_urls.append(new_url)
                if param == '':
                    pass
                else:
                    data = json.loads(param)  # 将参数转化为json格式
                results = requests.post(new_url, data=json.dumps(data), headers=headers).text
                responses.append(results)
                res = Result_check().interface_result_check(results, res_check)
            if 'pass' in res:
                res_flags.append('pass')
                count_success += 1
            else:
                logger.warning(u'接口返回结果与预期结果不一致!失败URL: %s METHOD :%s' % (url, method))
                res_flags.append('fail')
                count_failure += 1
                failed_case.append((interface_name, method, url))
        logger.info(u'共执行 %s 条用例,PASS: %s,FAILED: %s' % (len(case_list), count_success, count_failure))
        return res_flags, request_urls, responses, count_success, count_failure, failed_case

日志封装

# coding=utf-8
import logging
import sys
import traceback
import time
 
class LoggingUtils:
    '''
    ===========封装日志工具类的基本操作=============
    '''
    def __init__(self,logfile):
        '''
        :param logfile:
        '''
        self.logger = logging.getLogger(logfile)
        self.hdlr = logging.FileHandler(logfile)
        formatter = logging.Formatter('%(asctime)s %(levelname)s - %(message)s')
        self.ch = logging.StreamHandler()
        self.ch.setLevel(logging.INFO)
        self.ch.setFormatter(formatter)
        self.hdlr.setFormatter(formatter)
        self.logger.addHandler(self.hdlr)
        self.logger.addHandler(self.ch)
        self.logger.setLevel(logging.DEBUG)
 
    def debug(self, msg):
        '''
        :param msg:
        :return:
        '''
        self.logger.debug(msg)
        self.hdlr.flush()
 
    def info(self, msg):
        '''
         
        :param msg:
        :return:
        '''
        self.logger.info(msg)
        self.hdlr.flush()
 
    def warning(self,msg):
        self.logger.warning(msg)
        self.hdlr.flush()
 
    def error(self, msg):
        '''
 
        :param msg:
        :return:
        '''
        self.logger.error(msg)
        # self.logger.removeHandler(logging.StreamHandler())
        self.logger.removeHandler(self.ch)
        self.hdlr.flush()
 
    def error_sys(self, limit=None):
        '''
        :param limit:
        :return:
        '''
        exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
        if limit is None:
            if hasattr(sys, 'tracebacklimit'):
                limit = sys.tracebacklimit
        n = 0
        eline = '\n'
        while exceptionTraceback is not None and (limit is None or n < limit):
            f = exceptionTraceback.tb_frame
            lineno = exceptionTraceback.tb_lineno
            co = f.f_code
            filename = co.co_filename
            name = co.co_name
            eline += ' File "%s", line %d, in %s \n ' % (filename, lineno, name)
            exceptionTraceback = exceptionTraceback.tb_next
            n = n + 1
 
        eline += "\n".join(traceback.format_exception_only(exceptionType, exceptionValue))
        self.logger.error(eline)
        self.hdlr.flush()
timer = time.strftime('%Y-%m-%d',time.localtime())
logger = LoggingUtils('%s.log'%timer)

结果比对

#coding:utf-8
class result_check():
 
    def __init__(self):
        pass
 
    def result_check(self,results,res_check):
        '''
        结果对比函数
        '''
        #返回结果,将结果中的json数据转化为可以和预期结果比较的数据
        res = results.replace('":"','=').replace('" : "','=')
        #预期结果,是xx=11;xx=22
        res_check = res_check.split(';')
        for s in res_check:
            if s in res:
                pass
            else:
                return '结果不匹配 '+ str(s)
        return 'pass'
 result_save.py   保存测试结果的模块,复制原有的用例,保存为新的excel
#coding:utf-8
from xlutils import copy
import xlrd
import time
import os
 
class Save_test_result():
 
    def __init__(self):
        pass
 
    def save_result(self,file_path,res_flags,request_urls,responses):
        '''
        :return:
        '''
        book = xlrd.open_workbook(file_path)
        new_book = copy.copy(book)
        sheet = new_book.get_sheet(0)
        i = 1
        for request_url, response, flag in zip(request_urls, responses, res_flags):
            sheet.write(i, 8, u'%s' % request_url)
            sheet.write(i, 9, u'%s' % response)
            sheet.write(i, 10, u'%s' % flag)
            i += 1
        report_path = os.path.abspath(os.path.join('report'))
        if not os.path.exists(report_path):
            os.makedirs(report_path)
        new_book.save(os.path.abspath(os.path.join(report_path, 'Report@%s.xls' % time.strftime('%Y.%m.%d@%H%M%S'))))

结果邮件

#coding:utf-8
import smtplib
from email.mime.text import MIMEText
from email.header import Header
from email.mime.multipart import MIMEMultipart
import os
from logging_save import  logger
 
 
class Send_report(object):
    def __init__(self,count_success,count_failure,failed_case):
        '''
        :param count_success:
        :param count_failure:
        :param failed_case:
        '''
        self.count_success = count_success
        self.count_failure = count_failure
        self.failed_case = failed_case
 
    def newest_report(self,testreport='report'):
        '''
        获取最新的测试报告
        :param testreport:
        :return:
        '''
        lists = os.listdir(testreport)
        lists.sort(key=lambda fn: os.path.getmtime(os.path.join(testreport,fn)))
        file_new = os.path.join(testreport, lists[-1])
        logger.info('获取最新附件报告成功')
        return file_new
 
    def send_result(self,username,passwd,from_addr,to_addrs,smtpserver,*args):
        '''
        :param username:
        :param passwd:
        :param from_addr:
        :param to_addrs:
        :param smtpserver:
        :param args:
        :return:
        '''
        sender = from_addr
        subject = '财富港接口测试结果'
        username = username
        passwd = passwd
 
        '''邮件内容'''
        tille = (u'用例名称', u'请求方式', u'url')
        details = (u'成功: ' + str(self.count_success) + u'失败: ' + str(self.count_failure)) + '\n' + u'失败的用例如下 :' + \
                  '\n' + '\n'.join(str(zip(tille, i)) for i in self.failed_case).decode('unicode-escape')
        logger.info('邮件附件为: %s' %(args[0].split('\\')[1]))
 
        if args != None: #判断是否添加附件
            msg = MIMEMultipart()
            msg.attach(MIMEText(details, 'plain', 'utf-8'))
            i = 0
            while i < len(args): #可以添加多个附件
                part = MIMEText(open(args[i], 'rb').read(), 'base64', 'utf-8')
                part["Content-Type"] = 'application/octet-stream'
                part["Content-Disposition"] = 'attachment; filename="%s"'%args[i]
                msg.attach(part) #添加附件
                i += 1
 
            msg['subject'] = Header(subject, 'utf-8')
            msg['From'] = from_addr
            msg['To'] = ','.join(eval(to_addrs)) #兼容多个收件人
            smtp = smtplib.SMTP()
            try:
                smtp.connect(smtpserver)
                smtp.login(username, passwd)
                smtp.sendmail(sender, eval(to_addrs), msg.as_string())
                smtp.close()
                logger.info('带附件测试报告发送成功!')
            except smtplib.SMTPAuthenticationError,e:
                logger.error('邮箱账户或密码错误: '+ str(e))
 
        else:
            msg = MIMEText(details, 'plain', 'utf-8')
            msg['subject'] = Header(subject, 'utf-8')
            msg['From'] = from_addr
            msg['To'] =  ','.join(eval(to_addrs))
            smtp = smtplib.SMTP()
            try:
                smtp.connect(smtpserver)
                smtp.login(username, passwd)
                smtp.sendmail(sender, eval(to_addrs), msg.as_string())
                logger.info('测试报告发送成功!')
                smtp.close()
            except smtplib.SMTPAuthenticationError,e:
                logger.error('邮箱账户或密码错误 : '+str(e))

用例获取及数据格式化

#coding:utf-8
import xlrd
 
from logging_save import logger
 
class Get_testcase(object):
 
    def __init__(self, file_path):
        '''
        :param file_path: 用例文件路径
        '''
        self.file_path = file_path
 
    def readExcel(self):
        '''
        读取用例函数
        :return: 测试用例列表
        '''
        try:
            book = xlrd.open_workbook(self.file_path)  # 打开excel
        except Exception, error:
            logger.error('路径不在或者excel不正确 : ' + str(error))
            return error
        else:
            sheet = book.sheet_by_index(0)  # 取第一个sheet页
            rows = sheet.nrows  # 取这个sheet页的所有行数
            case_list = []  # 用于保存用例信息
            for i in range(rows):
                if i != 0:
                    case_list.append(sheet.row_values(i)) # 把每一条测试用例添加到case_list中
            return case_list

请求url转换

#coding:utf-8
class urltransform(object):
    def __init__(self):
        pass
 
    def urltransform(self, url, method, param):
        '''
        :return:
        '''
        if param == '':
            new_url = url
        else:
            if method.upper() == 'GET':
                new_url = url + '?' + param.replace(';', '&')  #如果有参数,且为GET方法则组装url
            else:
                new_url = url
        return new_url

测试用例excel结构

config目录下,config.py   获取配置文件信息的模块

#conding:utf-8
import ConfigParser
 
class Config(object):
 
    def __init__(self,file_path):
        self.config = ConfigParser.ConfigParser()
        self.config.read(file_path)
 
    def get_mail_config(self):
        login_user = self.config.get('SMTP', 'login_user')
        login_pwd = self.config.get('SMTP', 'login_pwd')
        from_addr = self.config.get('SMTP', 'from_addr')
        to_addrs = self.config.get('SMTP', 'to_addrs')
        smtp_server = self.config.get('SMTP', 'smtp_server')
        port = self.config.get('SMTP', 'port')
        return login_user, login_pwd , from_addr, to_addrs,smtp_server, port
 
    def report_save_config(self):
        pass

mail.conf

[SMTP]
login_user = 18******@163.com
login_pwd = ******
from_addr =  BI<18******@163.com>
to_addrs = ['18******@163.com']
#to_addrs = ['1******@qq.com','******.com']
smtp_server = smtp.163.com
port = 25

测试报告

邮件接收结果


资料获取方法

【留言777】

各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!

三连之后我会在评论区挨个私信发给你们~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/856374.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科聪控制系统六大行业典型应用案例合集

01. 纺织行业 科聪移动机器人通用控制系统赋能无人接丝AGV实现丝盘自动化上下料 应用难点&#xff1a;无人接丝AGV是纺织行业无人工厂重要环节之一&#xff0c;这个环节对机器人到点精度要求非常高&#xff0c;无人接丝AGV的到位精度&#xff0c;必须确保“丝级”的准确无误&…

geeemap学习总结(2)——地图底图应用

1. 加载库中已有图层 import os os.environ[HTTP_PROXY] http://127.0.0.1:8001 os.environ[HTTPS_PROXY] http://127.0.0.1:8001 # 设置中心位置/地图层级/图层加载高度&#xff0c;加载图层 import geemap Mapgeemap.Map(center[40, 100], zoom4, height600) Map# 添加已经…

Javascript 正则

基本语法 定义 JavaScript种正则表达式有两种定义方式 构造函数 var regnew RegExp(<%[^%>]%>,g);字面量 var reg/<%[^%>]%>/g;g&#xff1a; global&#xff0c;全文搜索&#xff0c;默认搜索到第一个结果接停止i&#xff1a;ingore case&#xff0c;忽略…

Linux(进程地址空间)

进程地址空间 程序地址空间进程地址空间 程序地址空间 在Linux环境下&#xff0c;我们可以对上述程序空间地址进行验证&#xff1a; 运行程序&#xff0c;可以看到&#xff0c;我们就可以很好看出程序的地址空间的排布了&#xff1a; 进程地址空间 严格来说&#xff0c;我们…

免费商用图片素材网站,4K高清无水印。

推荐6个图片素材网站&#xff0c;免费下载&#xff0c;还可以商用&#xff0c;希望对大家有帮助。 菜鸟图库 美女图片|手机壁纸|风景图片大全|高清图片素材下载网 - 菜鸟图库 网站主要是为新手设计师提供免费素材的&#xff0c;素材的质量都很高&#xff0c;类别也很多&#x…

NGINX组件(rewrite)

一、location匹配的规则和优先级&#xff08;*&#xff09; URI&#xff1a;统一资源标识符&#xff0c;是一种字符串标识&#xff0c;用于标识抽象的或者是物理资源&#xff1b;如&#xff1a;文件、图片、视频等 nginx中的URI匹配的是&#xff1a;网址”/“后的路径 如&…

17 spring项目——登录拦截器

经实验发现&#xff0c;当访问拦截器拦截的地址的时候都会经过拦截器。在拦截器中&#xff0c;返回true则放行&#xff0c;允许访问该地址&#xff1b;返回false&#xff0c;则拦截&#xff0c;不允许访问该地址&#xff0c;但可以在return前去设置要跳转的地址。 拦截器可以拦…

【二分】CF1623 C

Problem - 1623C - Codeforces 题意&#xff1a; 思路&#xff1a; 肯定是二分&#xff0c;我们去二分最小值&#xff0c;然后check的时候最小值要大于mid check的时候要让最小值尽可能大 注意到我们不需要去管最大值&#xff0c;只需要最小值尽可能大就好了&#xff0c;因…

Creo散热处理结构设计--阵列操作

问题描述&#xff1a; 在某一平面掏孔以散热&#xff0c;如何快速的绘制多孔并掏空处理 解决方式&#xff1a; 采用阵列操作。 1&#xff09;绘制圆、拉伸处理 2&#xff09;选择需要阵列的单元&#xff0c;选择阵列操作&#xff0c;在弹出的界面选择方向&#xff0c;按照…

BigDecimal(double)和BigDecimal(String)有什么区别?

面试回答 有区别&#xff0c;而且区别很大。 因为double是不精确的&#xff0c;所以使用一个不精确的数字来创建 BigDecimal&#xff0c;得到的数字也是不精确的。如 0.1 这个数字&#xff0c;double 只能表示他的近似值。 所以&#xff0c;当我们使用 new BigDecimal(0.1) 创建…

SwiftUI 兼容 Light Dark

1. Assets 右键 New Color Set 2. 起个合适的颜色名称&#xff0c;修改一下 demo_bgcolordemo_card_bgcolordemo_text_color 3. Show Me The Code // // light_dark_demo.swift // bill2 // // Created by 朱洪苇 on 2023/8/10. //import SwiftUIstruct light_dark_demo: …

机器学习---自编码器

自编码器过程 输入一个图片&#xff0c;经过encoder变成一个向量&#xff0c;再通过decoder将这个向量反向生成输入的图片。 这里我们希望输入和输出越接近越好。这个过程我们称为重建。 特点&#xff1a;不需要任何的标注资料。 在2006年这个思想就被提出来了&#xff1a; …

消息队列比较

、ActiveMQ 优点&#xff1a;单机吞吐量万级&#xff0c;时效性ms级&#xff0c;可用性高&#xff0c;基于主从架构实现高可用性&#xff0c;消息可靠性较低的概率丢失数据。 缺点&#xff1a;官方社区现在对ActiveMQ5.X维护越来越少了&#xff0c;高吞吐量场景较少使用。 2、K…

《Linux从练气到飞升》No.10 冯洛依曼体系结构

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

rust关于项目结构包,Crate和mod和目录的组织

rust 最近开始学习rust语言。感觉这门语言相对java确实是难上很多。开几个文章把遇到的问题记录一下 rust关于包&#xff0c;Crate 关于包&#xff0c;Crate这块先看看官方书籍怎么说的 crate 是 Rust 在编译时最小的代码单位。如果你用 rustc 而不是 cargo 来编译一个文件…

Opencv实战(银行卡识别)

目录 模版匹配不同方法对比一个模板匹配多个 直方图及直方图均衡化Mask操作直方图均衡化自适应直方图均衡化 模版匹配 公式 import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline def cv_show(img,nam…

win10 + VS2022 安装opencv C++

最近需要用到C opencv&#xff0c;看了很多帖子都需要自己编译opencv源码。为避免源码编译&#xff0c;可以使用VS来配置opencv C。下面是主要过程&#xff1a; 目录 1. 从官网下载 opencv - Get Started - OpenCV 2. 点击这个exe文件进行安装 3. 配置环境变量 4. VS中的项…

【数据结构与算法】多路查找树

多路查找树 二叉树的问题分析 二叉树的操作效率较高&#xff0c;但也存在问题。 二叉树需要加载到内存的&#xff0c;如果二叉树节点少&#xff0c;没什么问题&#xff0c;但是如果二叉树的节点很多&#xff08;比如 1 亿&#xff09;&#xff0c;就存在如下问题&#xff1a…

【信号生成器】从 Excel 数据文件创建 Simulink 信号生成器块研究(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

深度学习:使用卷积神经网络CNN实现MNIST手写数字识别

引言 本项目基于pytorch构建了一个深度学习神经网络&#xff0c;网络包含卷积层、池化层、全连接层&#xff0c;通过此网络实现对MINST数据集手写数字的识别&#xff0c;通过本项目代码&#xff0c;从原理上理解手写数字识别的全过程&#xff0c;包括反向传播&#xff0c;梯度…