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

目录

实现思路

统筹脚本

请求封装 

日志封装

结果比对

结果邮件

用例获取及数据格式化

请求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】

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Jay17 2023.8.10日报

笔记 【python反序列化】 序列化 类对象->字节流&#xff08;字符串&#xff09; 反序列化 字节流->对象 python反序列化没PHP这么灵活&#xff0c;没这么多魔术方法。 import pickle import os class ctfshow(): def init(self): self.username0 self.password0 d…

使用Prisma访问数据库

首先&#xff0c;确保你已经安装了 Prisma CLI。你可以使用以下命令进行安装&#xff1a; npm install prisma --save-dev接下来&#xff0c;你需要初始化 Prisma 项目&#xff0c;最后一个参数需要指定数据库类型&#xff0c;如postgresql&#xff0c;sqlist&#xff0c;mysql…

计算机视觉的应用9-视觉领域中的61个经典数据集【大集合】的应用与实战

大家好,我是微学AI,今天给大家介绍一下计算机视觉的应用9-视觉领域中的61个经典数据集【大集合】的应用与实战,我们都知道计算机视觉是一门研究如何使计算机能够理解和解释数字图像或视频的技术和方法。在计算机视觉领域中,数据集是非常重要的资源,它们可以用于训练和评估…

【ChatGPT】自我救赎

ChatGPT辅助学习C之【在C中如果大数据类型转小数据类型会发生什么呢?】&#xff0c;今天问ChatGPT一个问题&#xff0c;让它解析下面这个C程序&#xff1a; #include <iostream> #include <cstdio> using namespace std; int main() {int a;long long b532165478…

Linux-mysql安装

1. 获取rpm wget https://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm 2. 安装rpm rpm -ivh mysql57-community-release-el7-9.noarch.rpm 3. 确认依赖文件 cd /etc/yum.repos.d ls 查看该文件夹下是否已存在如下两个文件 4. import mysql 的公钥到RPM…

原型链污染例题复现

一、什么是原型链 下面我们通过这个小例子来看看。 可以看到b在实例化为test对象以后&#xff0c;就可以输出test类中的属性a了。这是因为在于js中的一个重要的概念&#xff1a;继承。而继承的整个过程就称为该类的原型链。 在javascript中,每个对象的都有一个指向他的原型(p…

07-2_Qt 5.9 C++开发指南_二进制文件读写(stm和dat格式)

文章目录 1. 实例功能概述2. Qt预定义编码文件的读写2.1 保存为stm文件2.2 stm文件格式2.3 读取stm文件 3. 标准编码文件的读写3.1 保存为dat文件3.2 dat文件格式3.3 读取dat文件 4. 框架及源码4.1 可视化UI设计4.2 mainwindow.cpp 1. 实例功能概述 除了文本文件之外&#xff…

怎样的公司称为集团?

集团是什么&#xff1f; 集团的意思是有目的组织起来共同行动的团体。企业集团不具有独立的法人资格。《公司法》中并没有“集团”一说。只有有限责任公司和股份有限公司的提法。有的公司进行多元化经营战略&#xff0c;在多个领域均成立了相应的子公司&#xff0c;这样&#…

PHP codeigniter4 搭配Nginx

> 主要是为了用Nginx运行PHP环境 1. Nginx 官方文档的配置 default.conf This configuration enables URLs without “index.php” in them and using CodeIgniter’s “404 - File Not Found” for URLs ending with “.php”. server {listen 80;listen [::]:80;se…

O2OA开发平台实施入门指南

O2OA&#xff08;翱途&#xff09;开发平台&#xff0c;是一款适用于协同办公系统开发与实施的基础平台&#xff0c;说到底&#xff0c;它也是一款快速开发平台。开发者可以基于平台提供的能力完成门户、流程、信息相关的业务功能开发。 既然定位为开发平台&#xff0c;那么开…

React 核心开发者 Dan Abramov 宣布从 Meta 离职

导读React.js 核心开发者、Redux 作者 Dan Abramov 在社交平台发文宣布&#xff0c;将辞去在 Meta 的职务&#xff1a; “我感到苦乐参半&#xff0c;几周后我就要辞去 Meta 的工作了。在 Meta 的 React 组织工作是我的荣幸。感谢我过去和现在的同事接纳我&#xff0c;容忍我犯…

编织人工智能:机器学习发展历史与关键技术全解析

文章目录 1. 引言1.1 机器学习的定义1.2 重要性和应用场景重要性应用场景 2. 机器学习的早期历史2.1 初期理论与算法感知机决策树 2.2 早期突破支持向量机神经网络初探 3. 21世纪初期的发展3.1 集成学习方法随机森林XGBoost 3.2 深度学习的崛起卷积神经网络&#xff08;CNN&…

linux cp -rpf指令

cp -rpf #强行递归复制/etc目录到/mist目录中&#xff0c;并保持源目录的权限等信息不变。 有点类似于打patch&#xff0c;不会改变已有的内容。

nodejs实现解析chm文件列表,无需转换为PDF文件格式,在线预览chm文件以及目录,不依赖任何网页端插件

特性: 1、支持任意深度的chm文件解析 2、解析后内容结构转换为tree数据呈现 3、点击树节点可以在html实时查看数据 4、不依赖任何浏览器端插件,兼容性较好 nodejs端核心代码 const $g = global.SG.$g, fs = global.SG.fs, router = global.SG.router, xlsx = global.SG.xl…

C++将函数声明放在头文件中的示例

C将函数声明放在头文件中的示例 C函数原型声明&#xff08;函数声明&#xff09;的位置可以有以下几种选择&#xff1a; 1.函数声明放在同一源文件中&#xff1a;这种情况通常适用较小的项目中&#xff0c;通常可以将函数的声明和定义放在同一个源文件中。先声明函数原型&…

2. Linux安装Git

yum安装 查看版本 版本太低&#xff0c;所以我们采用自己上传编译的方式进行 删除已安装的git yum remove git 下载最新安装包&#xff0c;并上传到服务器文件夹下 上传&#xff0c;解压 5.安装编译需要的依赖 yum install curl-devel expat-devel gettext-devel openssl-…

【Leetcode】层次遍历||树深度||队列

step by step. 题目&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&am…

动力节点2023版王鹤SpringBoot3学习教程

Spring Boot 3.0新版本的推出&#xff0c;带来个更多的新特性和功能&#xff0c;也为开发人员提供更高效、更优秀的开发体验和应用性能&#xff0c;值得我们深入学习。 动力节点最新升级版SpringBoot3教程&#xff0c;王鹤老师再度出击&#xff0c;视频长达28小时&#xff0c;够…

windows配置git公钥,读写远程git项目

首先Windows电脑需要下载并安装git&#xff1a; 从官网直接下载然后安装即可&#xff1a;https://git-scm.com/download/win 添加公钥 ssh-keygen -t rsa -C "xxxxxxx.com"注意&#xff1a;这个xxxxxxx.com与github注册的邮箱一致 然后一路回车&#xff0c;生成完…

如何使用Mac终端给树莓派pico构建C/C++程序进行开发,以及遇到各种问题该怎么处理,不使用任何IDE或编辑器(例如VS Code)

写本文的原因是官方的教程已经过时了&#xff0c;如果你现在按照官方教程来在 Mac 上进行配置&#xff0c;那么会遇到一堆问题&#xff0c;比如我几乎把能踩的“雷”都踩了。所以这里记录了完整过程&#xff0c;以及各种错误的原因和处理方法&#xff0c;不然以后换 Mac 了或者…