基于flask+react搭建测试用例管理平台
发布日期:2022-02-24 01:06:51
浏览次数:13
分类:技术文章
本文共 11845 字,大约阅读时间需要 39 分钟。
基于flask+react搭建测试用例管理平台
平台介绍
平台目前功能介绍
- 工作台(账户动态,项目管理,组织管理,系统设置,测试分析等)
- 功能测试用例管理(本文只介绍此模块)
- API自动化测试系统(接口自动化系统,场景配置,报告配置,用例管理等等)
- GUI自动化测试系统(UI自动化测试系统,场景配置,报告配置,用例管理等等,暂未开发完成)
- 测试工具管理(测试脚本,小工具管理)
- 知识库(知识分享、问题收集)
因部门工作中所产出的测试用例数据难以收集和查阅,后又因TestLink界面不够友好,难以推动使用。开发了测试用例管理系统
测试用例管理模块主要逻辑流程为 新建测试计划 > 新建计划内上线功能或需求 > 创建测试用例(或上传测试用例支持Xmind导入,Excel导出等)> 执行测试 > 测试完成 > 更改需求状态 > 更改测试计划状态效果展示-计划列表页
效果展示-计划详情页
使用到的技术栈 (测试用例管理)
后端
- 框架选择:Flask 、Tornado
- Python版本:3.6.5
- 数据库:MySQL、Redis
- 异步任务处理:celery
- 服务进程管理:Docker、uwsgi、Supervisor
前端
- 框架:Ant Design Pro(蚂蚁金服开源的基于 React 的中后台管理控制台的脚手架)
- nodejs版本:12.6.0
- react版本:16.7.0
后端服务介绍
- requirements 文件
colorlogFlaskflask-apidocFlask-HTTPAuthFlask-Migrateflask-paginateFlask-RESTfulFlask-ScriptFlask-SQLAlchemyitsdangeroussqlalchemygeventmysqlclientpycryptodomePyMySQLredisrequestssshtunneluWSGIWerkzeugceleryconcurrentxlwtxmindparser
- 服务结构 Python版本:3.6.5 编辑器:PyCharm 2018 1.3
- 文件目录介绍
目录&文件 | 作用 |
---|---|
Apis | 各系统接口主要代码存放路径,用Blueprint分开接口, flask_restful 设计接口 |
Common | 公共库,主要为应用工厂,日志,签名,celery任务消息队列处理,token认证等 |
Config | 项目配置 |
Models | 数据库相关model,用于ORM处理 |
DockerFile | 用于构建Docker容器 |
runserver | 项目启动入口 |
- 配置文件 Config 的__init__.py文件
# -*- coding: utf-8 -*- # @File : __init__.py# @Time : 2020/1/3 下午3:26# @Author : bin.wang# @Detail : 配置文件from sqlalchemy import create_enginefrom datetime import timedeltafrom Common.log.logger import LogFile, Logfrom concurrent.futures import ThreadPoolExecutorimport redisimport osclass Config: # 线程池, max_workers参数 不填默认为 CPU个数 * 5 threaded = True executor = ThreadPoolExecutor() # session过期时间 PERMANENT_SESSION_LIFETIME = timedelta(days=1) SECRET_KEY = os.urandom(24) # 为True ,无需 db.session.commit() SQLALCHEMY_TRACK_MODIFICATIONS = False # 上传文件大小限制 8M MAX_CONTENT_LENGTH = 8 * 1024 * 1024 UPLOAD_FOLDER = '/log/api/file/' ALLOWED_EXTENSIONS = { 'xmind'} # redis 配置 rds_host = 'localhost' rds_port = 6379 rds_db = 0 rds_pool_size = 300 redis_pool = redis.ConnectionPool(host=rds_host, port=rds_port, db=rds_db, max_connections=rds_pool_size) redis_db = redis.Redis(connection_pool=redis_pool) # celery任务队列服务端配置 CELERY_BROKER_URL = "redis://localhost:6379/0" CELERY_RESULT_BACKEND = "redis://localhost:6379/1" # 响应内容配置 success_status = "SUCCESS" success_message = "请求成功" fail_status = "FAIL" fail_message_body = "请求参数错误" fail_message_server = "请求异常" fail_message_path = "非法请求" fail_message_no_found = "查询为空" # 测试环境数据库配置 test_host = "xxxxxxxxxx" test_port = xxxxxxxxxx test_username = "xxxxxxxxxx" test_password = "xxxxxxxxxx" test_charset = "utf8" test_database = "xxxxxxxxxx" test_mysql_uri = create_engine('mysql+mysqldb://{}:{}@{}:{}/{}?charset=utf8'.format(test_username, test_password, test_host, test_port, test_database))class ProductionConfig(Config): # 生产 DEBUG = False AUTH_SALT = "xxxxxxxxxx" # token加盐字符串 HOSTNAME = 'xxxxxxxxxx' PORT = 'xxxxxxxxxx' DATABASE = 'xxxxxxxxxx' USERNAME = 'xxxxxxxxxx' PASSWORD = 'xxxxxxxxxx' DB_URI = 'mysql+mysqldb://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE) SQLALCHEMY_DATABASE_URI = DB_URI # 输出SQL语句 SQLALCHEMY_ECHO = False # 用例导出生成文件地址 EXCELPATH = "/xxxxxxxxxx/nginx/www/static/files/" # 日志配置 log_file_path = "/xxxxxxxxxx/log/api/flask/" log = LogFile(logger_name=__name__, debug=False, logfile_path=log_file_path).log()class DevelopmentConfig(Config): # 测试 DEBUG = True AUTH_SALT = "xxxxxxxxxx" HOSTNAME = 'xxxxxxxxxx' PORT = 'xxxxxxxxxx' DATABASE = 'xxxxxxxxxx' USERNAME = 'xxxxxxxxxx' PASSWORD = 'xxxxxxxxxx' DB_URI = 'mysql+mysqldb://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE) SQLALCHEMY_DATABASE_URI = DB_URI # 输出SQL语句 SQLALCHEMY_ECHO = True # 用例导出生成文件地址 EXCELPATH = "/xxxxxxxxxx/log/api/static/" # 控制台日志配置 log_file_path = "/xxxxxxxxxx/log/api/flask/" log = LogFile(logger_name=__name__, debug=False, logfile_path=log_file_path).log() # log = Log(logger_name=__name__, debug=True).logger_stream()config = { "Dev": DevelopmentConfig, "Pro": ProductionConfig}
- Config的 setting文件
from Config import configserverConfig = config["Pro"]## 这里控制服务启动哪个环境
- runserver文件,应用启动入口
# -*- coding: utf-8 -*- # @File : main# @Time : 2019/9/16 上午10:06# @Author : bin.wang# @Detail :from gevent.pywsgi import WSGIServerfrom gevent import monkeymonkey.patch_all()from Common.Application import create_appfrom flask import request, jsonifyfrom Config.setting import serverConfigapp = create_app(serverConfig)log = serverConfig.log@app.errorhandler(404)def not_found(error): return jsonify(status=serverConfig.fail_status, message=serverConfig.fail_message_path, data=request.path)@app.before_requestdef request_before(): log.info({ "request_path": request.path, "request_body": request.json, "request_args": request.args.to_dict()})if __name__ == '__main__': http_server = WSGIServer(('', 5000), app) http_server.serve_forever()
用例导入
Python xmindparser库实现,思路:
- 后端提供上传文件接口,xmindparser读取 xmind文件,转成字典
- 递归去寻找每一层,并拼接多级子模块为用例标题,(步骤结果可不填)
- 把找到的用例加入列表,最终直接批量异步插入数据库
- 写入用例完成,删除上传的xmind文件,失败也删除
-
示例:
xmind文件:上传成功:
用例导出
Python xlwt库实现,思路:
- 后端接收需要导出的用例ID列表
- 查询出结果,根据需求归类(用于合并单元格计算),准备好数据列表
- 传递给封装好的Excel处理函数,并准备多套xlwt样式,根据不同主题展示不同内容
- 写入文件完成,生成静态文件路径给前端,前端下载
示例:
前端服务介绍
前端框架介绍
Ant Design Pro 是一个企业级中后台前端/设计解决方案,我们秉承 Ant Design 的设计价值观,致力于在设计规范和基础组件的基础上,继续向上构建,提炼出典型模板/业务组件/配套设计资源,进一步提升企业级中后台产品设计研发过程中的『用户』和『设计者』的体验。随着『设计者』的不断反馈,我们将持续迭代,逐步沉淀和总结出更多设计模式和相应的代码实现,阐述中后台产品模板/组件/业务场景的最佳实践,也十分期待你的参与和共建。
我们基于上述目标和提供了以下的典型模板,并据此构建了一套基于 React 的中后台管理控制台的脚手架,它可以帮助你快速搭建企业级中后台产品原型。
——来自
目录结构
├── config # umi 配置,包含路由,构建等配置├── mock # 本地模拟数据├── public│ └── favicon.png # Favicon├── src│ ├── assets # 本地静态资源│ ├── components # 业务通用组件│ ├── e2e # 集成测试用例│ ├── layouts # 通用布局│ ├── models # 全局 dva model│ ├── pages # 业务页面入口和常用模板│ ├── services # 后台接口服务│ ├── utils # 工具库│ ├── locales # 国际化资源│ ├── global.less # 全局样式│ └── global.js # 全局 JS├── tests # 测试工具├── README.md└── package.json
部署生产环境
- 后端:Jenkins部署代码,Supervisor重启服务
- 前端:将构建后的文件作为静态资源部署在nginx中即可
Nginx配置
upstream baseService { server base:9090 weight=1 max_fails=3 fail_timeout=30s;}upstream toolsService { server xxxx:5000 weight=1 max_fails=3 fail_timeout=30s;}upstream Supervisor { server xxxx:9001 weight=1 max_fails=3 fail_timeout=30s;}server { server_name localhost; listen 80 default_server; listen [::]:80 default_server ipv6only=on; gzip on; gzip_min_length 1k; gzip_comp_level 9; gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml image/jpeg image/gif image/png; gzip_vary on; gzip_disable "MSIE [1-6]\."; charset UTF-8; access_log /var/log/nginx/access.log main; root /usr/share/nginx/html; index index.html index.htm; location / { try_files $uri $uri/ /index.html; } location /api/flask/ { auth_request /auth; auth_request_set $auth_status $upstream_status; auth_request_set $user $upstream_http_x_forwarded_user; error_page 401 = @error401; proxy_pass http://toolsService/api/; proxy_redirect off; proxy_connect_timeout 600; proxy_send_timeout 900; proxy_read_timeout 900; proxy_buffer_size 32k; proxy_buffers 4 64k; proxy_busy_buffers_size 128k; proxy_redirect off; proxy_hide_header Vary; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Accept-Encoding ''; proxy_set_header Referer $http_referer; proxy_set_header Cookie $http_cookie; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Original-URI $request_uri; proxy_next_upstream error timeout invalid_header http_404 http_500 http_502 http_503 http_504; } location /api/open/ { proxy_pass http://baseService/open/; proxy_connect_timeout 600; proxy_send_timeout 900; proxy_read_timeout 900; proxy_buffer_size 32k; proxy_buffers 4 64k; proxy_busy_buffers_size 128k; proxy_redirect off; proxy_hide_header Vary; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Accept-Encoding ''; proxy_set_header Referer $http_referer; proxy_set_header Cookie $http_cookie; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Original-URI $request_uri; proxy_next_upstream error timeout invalid_header http_404 http_500 http_502 http_503 http_504; } location /api/admin/ { auth_request /auth; auth_request_set $auth_status $upstream_status; auth_request_set $user $upstream_http_x_forwarded_user; error_page 401 = @error401; proxy_pass http://baseService/api/; proxy_connect_timeout 600; proxy_send_timeout 900; proxy_read_timeout 900; proxy_buffer_size 32k; proxy_buffers 4 64k; proxy_busy_buffers_size 128k; proxy_redirect off; proxy_hide_header Vary; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Accept-Encoding ''; proxy_set_header Referer $http_referer; proxy_set_header Cookie $http_cookie; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Original-URI $request_uri; proxy_next_upstream error timeout invalid_header http_404 http_500 http_502 http_503 http_504; } location = /auth { internal; proxy_pass http://baseService/auth/token; proxy_connect_timeout 600; proxy_send_timeout 900; proxy_read_timeout 900; proxy_buffer_size 32k; proxy_buffers 4 64k; proxy_busy_buffers_size 128k; proxy_redirect off; proxy_hide_header Vary; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Accept-Encoding ''; proxy_set_header Referer $http_referer; proxy_set_header Cookie $http_cookie; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Original-URI $request_uri; proxy_next_upstream error timeout invalid_header http_404 http_500 http_502 http_503 http_504; proxy_pass_request_body off; proxy_set_header Content-Type "application/json"; proxy_set_header Content-Length ""; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ { #设置上面定义的后缀文件缓存到浏览器的生存时间 expires 3d; # #禁止缓存,每次都从服务器请求 # add_header Cache-Control no-store; } location @error401 { default_type application/json; return 401 '{"status":"FAIL","message":"登录超时, 请重新登录", "data":""}'; } client_max_body_size 50M; keepalive_timeout 10;}
写在后面
还没想好,哈哈哈
转载地址:https://blog.csdn.net/weixin_45877986/article/details/103857030 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年04月10日 11时09分47秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
HTTP响应头不缓存
2021-06-30
apache的keepalive和keepalivetimeout(apache优化)
2021-06-30
内容协商 (Content Negotiation)
2021-06-30
关于URL编码
2021-06-30
HTTP中的缓存
2021-06-30
Varnish 和 Squid比较到底强多少
2021-06-30
mysql常用语句集锦
2019-04-27
PHP的Smarty
2019-04-27
ecshop模板的原理分析
2019-04-27
深入探究Smarty模版
2019-04-27
PHP高效率写法(详解原因)
2019-04-27
PHP数组实际占用内存大小的分析
2019-04-27
PHP运行模式
2019-04-27
MYSQL的MERGE存储引擎
2019-04-27
mysql 压力测试脚本
2019-04-27
PHP代码保护——Zend Guard
2019-04-27
Javassist 使用指南
2019-04-27
Unix下C程序内存泄漏检测工具Valgrind安装与使用
2019-04-27
DFA和NFA
2019-04-27
CentOS装机必备-基本设置以及缺失文件
2019-04-27