flask + Python3 实现的的API自动化测试平台---- IAPTest接口测试平台,更名:FXTest 接受定制开发(java版开发完毕)

简介: 笔记

**背景: 1.平时测试接口,总是现写代码,对测试用例的管理,以及测试报告的管理持久化做的不够,

             2.工作中移动端开发和后端开发总是不能并行进行,需要一个mock的依赖来让他们并行开发。

             3.同时让自己锻炼去开发测试平台,掌握flask开发程序,提高自己的业务水平。



整体思路:    1.利用flask+bootstrap来进行web界面开发,对接口,接口测试用例,定时任务,测试报告的持续集成。

                          2.IAPTest支持接口用例管理,接口多用例测试,支持定时测试任务,测试报告持久化

                          3.目前mock服务支持单一path,定时任务可以开启暂停多用例执行,定时任务执行后自动发送测试报告,多用例的单次执行,单接口的调试功能。对测试环境的管理

下面来看下最后的效果图,以及附上github开源地址。


测试环境管理界面:


3.png

 


定时任务界面:


 

4.png

 


mock界面

5.png



 


测试报告界面


6.png

 


用例管理界面

7.png

 


接口管理界面


8.png

 


**核心代码分享区:**


定时任务对应视图开发

class AddtimingtaskView(MethodView):
    @login_required
    def get(self):
        return  render_template('addtimingtasks.html')
    @login_required
    def post(self):
        taskname=request.form['taskname']
        tinmingtime=request.form['time']
        to_email_data=request.form['to_email']
        cao_email=request.form['cao_email']
        weihu=request.form['weihu']
        if taskname =='':
            flash('任务名不能为空!')
            return render_template('addtimingtasks.html')
        if tinmingtime =='':
            flash('任务执行时间不能为空!')
            return render_template('addtimingtasks.html')
        if to_email_data=='':
            flash('发送给谁邮件不能为空!')
            return render_template('addtimingtasks.html')
        if weihu=='':
            flash('维护人邮件不能为空!')
            return render_template('addtimingtasks.html')
        taskname_is = Task.query.filter_by(taskname=taskname).first()
        if taskname_is:
            flash('任务已经存在请重新填写!')
            return render_template('addtimingtasks.html')
        new_task=Task(taskname=taskname,taskstart=tinmingtime,taskrepor_to=to_email_data,taskrepor_cao=cao_email,task_make_email=weihu,
                      makeuser=current_user.id)
        db.session.add(new_task)
        try:
            db.session.commit()
            flash('添加定时任务成功')
            return  redirect(url_for('timingtask'))
        except Exception as e:
            db.session.rollback()
            flash('添加过程貌似异常艰难!')
            return redirect(url_for('addtimingtasks'))
        return render_template('addtimingtasks.html')
class Editmingtaskview(MethodView):
    @login_required
    def get(self,id):
        task_one=Task.query.filter_by(id=id).first()
        procjet=Project.query.all()
        if not task_one:
            flash('你编辑的不存在')
            return  redirect(url_for('timingtask'))
        return  render_template('Edittimingtasks.html',task_one=task_one,porjects=procjet)
    def post(self,id):
        task_one = Task.query.filter_by(id=id).first()
        procjet = Project.query.all()
        taskname = request.form['taskname']
        tinmingtime = request.form['time']
        to_email_data = request.form['to_email']
        cao_email = request.form['cao_email']
        weihu = request.form['weihu']
        if taskname =='':
            flash('任务名不能为空!')
            return render_template('addtimingtasks.html')
        if tinmingtime =='':
            flash('任务执行时间不能为空!')
            return render_template('addtimingtasks.html')
        if to_email_data=='':
            flash('发送给谁邮件不能为空!')
            return render_template('addtimingtasks.html')
        if weihu=='':
            flash('维护人邮件不能为空!')
            return render_template('addtimingtasks.html')
        task_one.taskname=taskname
        task_one.taskrepor_to=to_email_data
        task_one.taskrepor_cao=cao_email
        task_one.task_make_email=weihu
        task_one.makeuser=current_user.id
        try:
            db.session.commit()
            flash('编辑成功')
            return  redirect(url_for('timingtask'))
        except:
            db.session.rollback()
            flash('编辑出现问题!')
            return redirect(url_for('timingtask'))
        return render_template('Edittimingtasks.html', task_one=task_one,porjects=procjet)
class DeteleTaskViee(MethodView):
    def get(self,id):
        task_one = Task.query.filter_by(id=id).first()
        if not task_one:
            flash('你编辑的不存在')
            return redirect(url_for('timingtask'))
        if task_one.status==True:
            flash('已经删除')
            return redirect(url_for('timingtask'))
        task_one.status=True
        try:
            db.session.commit()
            flash('删除任务成功')
            return redirect(url_for('timingtask'))
        except:
            db.session.rollback()
            flash('删除任务休息了')
            return redirect(url_for('timingtask'))
@app.route('/gettest',methods=['POST'])
@login_required
def gettest():#ajax获取项目的测试用例
    projec=(request.get_data('project')).decode('utf-8')
    if not projec:
        return []
    proje=Project.query.filter_by(project_name=str(projec)).first()
    if not proje:
        return  []
    testyong=InterfaceTest.query.filter_by(projects_id=proje.id).all()
    testyong_list=[]
    for i in testyong:
        testyong_list.append({'name':i.Interface_name,'id':i.id})
    return   jsonify({'data':testyong_list})
class TestforTaskView(MethodView):#为测试任务添加测试用例
    def get(self,id):
        procjet = Project.query.all()
        task_one=Task.query.filter_by(id=id).first()
        return  render_template('addtestyongfortask.html',task_one=task_one,procjets=procjet)
    def post(self,id):
        procjet = Project.query.all()
        task_one = Task.query.filter_by(id=id).first()
        proc_test=request.form.get('project')
        if proc_test =='':
            flash(u'不能不添加测试项目!')
            return render_template('addtestyongfortask.html', task_one=task_one, procjets=procjet)
        test_yongli=request.form.getlist('testyongli')
        if test_yongli=='':
            flash(u'亲你见过只有测试项目没有测试用例的测试任务吗!')
            return render_template('addtestyongfortask.html', task_one=task_one, procjets=procjet)
        for oldtask in task_one.interface.all():
            task_one.interface.remove(oldtask)
        task_one.prject=Project.query.filter_by(project_name=proc_test).first().id
        for yongli in test_yongli:
            task_one.interface.append(InterfaceTest.query.filter_by(id=yongli).first())
            db.session.add(task_one)
        try:
            db.session.commit()
            flash('任务更新用例成功')
            return  redirect(url_for('timingtask'))
        except:
            flash('任务更新用例失败')
            return redirect(url_for('timingtask'))
        return render_template('addtestyongfortask.html', task_one=task_one, procjets=procjet)
class StartTaskView(MethodView):#开始定时任务
    @login_required
    def get(self,id):
        task=Task.query.filter_by(id=id).first()
        if len(task.interface.all())<=1:
            flash('定时任务执行过程的测试用例为多用例,请你谅解')
            return  redirect(url_for('timingtask'))
        try:
            scheduler.add_job(func=addtask, id=str(id), args=str(id),trigger=eval(task.taskstart),replace_existing=True)
            task.yunxing_status='启动'
            db.session.commit()
            flash(u'定时任务启动成功!')
            return  redirect(url_for('timingtask'))
        except Exception as e:
            flash(u'定时任务启动失败!请检查任务的各项内容各项内容是否正常')
            return redirect(url_for('timingtask'))
class ZantingtaskView(MethodView):#暂停定时任务
    @login_required
    def get(self,id):
        task = Task.query.filter_by(id=id).first()
        try:
            scheduler.pause_job(str(id))
            task.yunxing_status = '暂停'
            db.session.commit()
            flash(u'定时任务暂停成功!')
            return redirect(url_for('timingtask'))
        except:
            task.yunxing_status = '创建'
            db.session.commit()
            flash(u'定时任务暂停失败!已经为您初始化')
            return redirect(url_for('timingtask'))
class HuifutaskView(MethodView):#回复定时任务
    @login_required
    def get(self,id):
        task = Task.query.filter_by(id=id).first()
        try:
            scheduler.resume_job(str(id))
            task.yunxing_status='启动'
            db.session.commit()
            flash(u'定时任务恢复成功!')
            return redirect(url_for('timingtask'))
        except:
            task.yunxing_status = '创建'
            db.session.commit()
            flash(u'定时任务恢复失败!已经为您初始化')
            return redirect(url_for('timingtask'))
class YichuTaskView(MethodView):#移除定时任务
    @login_required
    def get(self,id):
        task = Task.query.filter_by(id=id).first()
        try:
            scheduler.delete_job(str(id))
            task.yunxing_status='关闭'
            db.session.commit()
            flash(u'定时任务移除成功!')
            return redirect(url_for('timingtask'))
        except:
            task.yunxing_status = '创建'
            db.session.commit()
            flash(u'定时任务移除失败!已经为您初始化')
            return redirect(url_for('timingtask'))

定时任务所执行的func代码

def addtask(id):#定时任务执行的时候所用的函数
    in_id=int(id)
    task=Task.query.filter_by(id=in_id).first()
    starttime = datetime.datetime.now()
    star = time.time()
    day = time.strftime("%Y%m%d%H%M", time.localtime(time.time()))
    basedir = os.path.abspath(os.path.dirname(__file__))
    file_dir = os.path.join(basedir, 'upload')
    file = os.path.join(file_dir, (day + '.log'))
    if os.path.exists(file) is False:
        os.system('touch %s' % file)
    filepath = os.path.join(file_dir, (day + '.html'))
    if os.path.exists(filepath) is False:
        os.system(r'touch %s' % filepath)
    projecct_list = []
    model_list = []
    Interface_name_list = []
    Interface_url_list = []
    Interface_meth_list = []
    Interface_pase_list = []
    Interface_assert_list = []
    Interface_headers_list = []
    id_list = []
    for task_yongli in task.interface.all():
        id_list.append(task_yongli.id)
        projecct_list.append(task_yongli.projects)
        model_list.append(task_yongli.models)
        Interface_url_list.append(task_yongli.Interface_url)
        Interface_name_list.append(task_yongli.Interface_name)
        Interface_meth_list.append(task_yongli.Interface_meth)
        Interface_pase_list.append(task_yongli.Interface_pase)
        Interface_assert_list.append(task_yongli.Interface_assert)
        Interface_headers_list.append(task_yongli.Interface_headers)
    apitest = ApiTestCase(Interface_url_list, Interface_meth_list, Interface_pase_list, Interface_assert_list, file,
                          Interface_headers_list)
    result_toal, result_pass, result_fail, relusts, bask_list = apitest.testapi()
    endtime = datetime.datetime.now()
    end = time.time()
    createHtml(titles=u'接口测试报告', filepath=filepath, starttime=starttime, endtime=endtime, passge=result_pass,
               fail=result_fail, id=id_list, name=projecct_list, headers=Interface_headers_list,
               coneent=Interface_url_list, url=Interface_meth_list, meth=Interface_pase_list,
               yuqi=Interface_assert_list, json=bask_list, relusts=relusts)
    hour = end - star
    user_id = User.query.filter_by(role_id=2).first().id
    new_reust = TestResult(Test_user_id=user_id, test_num=result_toal, pass_num=result_pass, fail_num=result_fail,
                           test_time=starttime, hour_time=hour, test_rep=(day + '.html'), test_log=(day + '.log'))
    email = EmailReport.query.filter_by(role_id=2, default_set=True).first()
    send_emails(sender=email.send_email, receivers=task.taskrepor_to, password=email.send_email_password,
                smtp=email.stmp_email, port=email.port, fujian1=file, fujian2=filepath, subject=u'%s自动用例执行测试报告' % day,
                url='%stest_rep'%(request.url_root))
    db.session.add(new_reust)
    db.session.commit()

mock服务的一个请求方式的代码

class MakemockserverView(MethodView):#做一个mock服务
    def get(self,path):#get请求方法
        huoqupath=Mockserver.query.filter_by(path=path,status=True).first()
        heders=request.headers
        method=request.method
        if not huoqupath:
            abort(404)
        if method.lower() !=huoqupath.methods:
            return  jsonify({'code':'-1','message':'请求方式错误!','data':''})
        if huoqupath.is_headers==True:
            if comp_dict(heders,huoqupath.headers) ==True:
                if huoqupath.ischeck==True:
                    paerm = request.values.to_dict()
                    if dict_par(paerm,huoqupath.params)==True:
                        if huoqupath.rebacktype == 'json':
                            try:
                                json_fan = json.dumps(huoqupath.fanhui)
                                return jsonify({'code': '1', 'message': 'successs', 'data': json_fan})
                            except:
                                return jsonify({'code': '-2', 'message': '你写入的返回不能正常json!请检查', 'data': ''})
                        elif huoqupath.rebacktype == 'xml':
                            response = make_response(huoqupath.fanhui)
                            response.content_type = 'application/xml'
                            return response
                        else:
                            return jsonify({'code': '-2', 'message': '你写入的类型目前系统不支持', 'data': ''})
                    else:
                        return jsonify({'code': '-4', 'message': '你输入的参数不正确', 'data': ''})
                else:
                    if huoqupath.rebacktype=='json':
                        try:
                            json_fan=json.dumps(huoqupath.fanhui)
                            return  jsonify({'code': '1', 'message': 'successs', 'data':json_fan})
                        except:
                            return jsonify({'code': '-2', 'message': '你写入的返回不能正常json!请检查', 'data': ''})
                    elif huoqupath.rebacktype =='xml':
                        response=make_response(huoqupath.fanhui)
                        response.content_type='application/xml'
                        return response
                    else:
                        return jsonify({'code': '-2', 'message': '你写入的类型目前系统不支持', 'data': ''})
            else:
                return jsonify({'code': '-3', 'message': '安全校验失败!', 'data': ''})
        if huoqupath.ischeck == True:
            paerm = request.values.to_dict()
            if dict_par(paerm, huoqupath.params) == True:
                if huoqupath.rebacktype == 'json':
                    try:
                        json_fan = json.dumps(huoqupath.fanhui)
                        return jsonify({'code': '1', 'message': 'successs', 'data': json_fan})
                    except:
                        return jsonify({'code': '-2', 'message': '你写入的返回不能正常json!请检查', 'data': ''})
                elif huoqupath.rebacktype == 'xml':
                    response = make_response(huoqupath.fanhui)
                    response.content_type = 'application/xml'
                    return response
                else:
                    return jsonify({'code': '-2', 'message': '你写入的类型目前系统不支持', 'data': ''})
            else:
                return jsonify({'code': '-4', 'message': '你输入的参数不正确', 'data': ''})
        else:
            if huoqupath.rebacktype == 'json':
                try:
                    json_fan = json.dumps(huoqupath.fanhui)
                    return jsonify({'code': '1', 'message': 'successs', 'data': json_fan})
                except:
                    return jsonify({'code': '-2', 'message': '你写入的返回不能正常json!请检查', 'data': ''})
            elif huoqupath.rebacktype == 'xml':
                response = make_response(huoqupath.fanhui)
                response.content_type = 'application/xml'
                return response
            else:
                return jsonify({'code': '-2', 'message': '你写入的类型目前系统不支持', 'data': ''}) #

开源地址:https://github.com/liwanlei/FXTest


使用说明:


1.依赖包为requirements.txt文件下的,可能部分不全,需要什么可以自己使用中增加。

2.目前由于考虑后续迁移内部使用的话,所以移除了注册功能,改为管理员后台添加方式,默认登录:liwanlei  密码:liwanlei

3.部分功能调试还存在一定的问题,欢迎各位多提宝贵意见,

部分功能欠缺:


1.定时任务的持久化,现在处理容易受到运行过程中的宕机等情况重新启动服务器的定时任务全部需要开启

2、mock接口只能支持单一的path

3. 测试环境没有改为动态配置,动态支持。目前测试环境管理以及上线

4。部分地方可能还会有不严谨性,但是工具可以使用。

5、目前仅支持 http请求中的json格式的,

6.大家可以多提意见,后续会优化,最近一直熬夜加班可能有些消息不能及时回复,还望谅解。


相关文章
|
2天前
|
JavaScript 测试技术 API
探索后端开发:构建高效API的艺术
【9月更文挑战第8天】本文旨在揭示后端开发中一个经常被忽视的领域——API设计。通过深入浅出的方式,我们将探讨如何构建一个既高效又易于维护的API。文章将涵盖设计原则、最佳实践以及一些常见的陷阱和解决方案。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和实用的技巧,帮助你在后端开发的道路上更进一步。
|
3天前
|
前端开发 API 开发者
深入浅出:后端开发中的API设计艺术
在数字化时代的浪潮中,后端开发如同搭建一座座数据桥梁,连接着用户与服务的无限可能。而API设计,则是这座桥梁的精髓所在。本文将带领读者领略API设计的艺术,从基础原则到进阶实践,探索如何打造高效、稳定且易于使用的后端接口。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往高质量后端开发的大门。
|
2天前
|
存储 安全 API
探索后端开发:构建高效API的艺术
【9月更文挑战第9天】在数字时代的浪潮中,后端开发如同一位默默无闻的艺术家,精心雕琢着每一个数据交互的细节。本文将带你走进后端的世界,从基础概念到实战技巧,一起学习如何打造高效、稳定且易于扩展的API。我们将通过深入浅出的方式,探讨后端开发的哲学与实践,让你在编码之旅中,找到属于自己的节奏和和谐。让我们一起跟随代码的脚步,解锁后端开发的无限可能。
|
6天前
|
XML JSON 缓存
探索后端开发中的RESTful API设计原则
【9月更文挑战第5天】在数字化时代的浪潮中,后端开发扮演着支撑整个互联网世界运行的基石角色。而作为连接前端与后端桥梁的RESTful API,其设计质量直接影响到应用的性能和用户体验。本文将深入探讨RESTful API的设计原则,通过浅显易懂的语言和实际代码示例,引导读者理解如何构建高效、易于维护的API。无论你是初学者还是有经验的开发者,这篇文章都将为你提供新的视角和思考。
|
7天前
|
数据采集 Java 数据挖掘
Java IO异常处理:在Web爬虫开发中的实践
Java IO异常处理:在Web爬虫开发中的实践
|
6天前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
27 2
|
8天前
|
测试技术 API 数据库
电商API接口定制与开发系列之——商品详情接口介绍
——在成长的路上,我们都是同行者。这篇关于API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦! 在电商API接口定制与开发系列中,商品详情接口是至关重要的一部分,它直接关系到用户浏览商品、获取商品信息的关键环节。以下是对商品详情接口的详细介绍:
|
Java API
Java 8 Stream API详解
版权声明:本文为博主chszs的原创文章,未经博主允许不得转载。 https://blog.csdn.net/chszs/article/details/47038607 Java ...
1016 0
|
28天前
|
机器人 API Python
智能对话机器人(通义版)会话接口API使用Quick Start
本文主要演示了如何使用python脚本快速调用智能对话机器人API接口,在参数获取的部分给出了具体的获取位置截图,这部分容易出错,第一次使用务必仔细参考接入参数获取的位置。
|
13天前
|
存储 JSON API
淘系API接口(解析返回的json数据)商品详情数据解析助力开发者
——在成长的路上,我们都是同行者。这篇关于商品详情API接口的文章,希望能帮助到您。期待与您继续分享更多API接口的知识,请记得关注Anzexi58哦! 淘宝API接口(如淘宝开放平台提供的API)允许开发者获取淘宝商品的各种信息,包括商品详情。然而,需要注意的是,直接访问淘宝的商品数据API通常需要商家身份或开发者权限,并且需要遵循淘宝的API使用协议。
淘系API接口(解析返回的json数据)商品详情数据解析助力开发者