1、自动化

某一天你入职了一家高大上的科技公司,开心的做着软件测试的工作,每天点点点,下班就走,晚上陪女朋友玩王者,生活很惬意。

但是美好时光一般不长,这种生活很快被女主管打破。为了提升公司测试效率,公司决定引入自动化流程,你在网上搜了一套技术方案 python + selenium,迅速写了一套自动化测试的脚本。

from selenium import webdriver

def test_selenium():
driver = webdriver.Firefox()
driver.get("http://www.baidu.com")
...
driver.quit()
...

编写脚本的日子很累,你需要每天加班,而且没有加班工资。 虽然如此,你也没有太多怨言,因为你能明显感觉到自己一点点掌握了自动化测试的流程,正在踏入职业发展的新阶段。这套脚本很快用于公司的主流程测试,也会在回归测试中使用。

因为大量的重复劳动都可以用这套自动化测试脚本代替,于是你又有时间陪女朋友了,上班也可以偶尔划水了,也可以时不时瞄一瞄自己的基金有没有涨。

当然,美好时光一般不长。在一次大改版中,前端页面发生了大量变化,你的自动化测试代码因为没有做抽象封装,基本已经不能用了。

又可以加班了,生活又可以充实起来了。你动用了一些像 PageObject 的模式对代码进行了重新设计,也加入了关键字驱动,尽量让测试逻辑变成可配置的。 设计完成以后,当前端页面变化时,只需要重点维护关键字表格。

你又为公司做了一些贡献,你已经完全胜任自动化测试的工作,甚至能够带一两个小弟。他们时不时找你问一些问题,但是对于自动化的维护工作还是要靠你自己,当你请假时,这些工作只能停滞。于是公司希望你做一些改进,让功能测试人员也可以运行这些自动化测试。

2、开始测试平台

你看到网上有很多人提到测试平台,想着自己也可以做一个可视化平台,这样功能测试人员也可以通过在界面上进行简单的设置,就可以使用底层的自动化代码了。很快 flask 出现在你的视线中,你做的第一个功能就是实现类似于 jenkins 的构建功能。

首先,你搭建了一个 flask 服务,服务启动后,你能顺利访问 5000 端口。

from flask import Flask
app = Flask(__name__)
app.run(port=5000)

然后,你配置了一个 url 地址,当访问这个 url 地址时,服务会调用一个函数,这个 url 和函数的绑定关系就是路由。函数的返回值可以是普通字符串,可以是 json 数据,也可以是 html 页面。

@app.route('/')
def index():
"show all projects in workspace dir"
workspace = pathlib.Path(app.root_path) / 'workspace'
projects = [project.name for project in workspace.iterdir()]
return render_template('index.html', projects=projects)

上面的代码就是模仿 jenkins, 把自动化测试的脚本放在项目的 workspace 目录下,当访问 / 根路径时,index 函数就会被调用。index 函数的作用就是列举 workspace 目录下的所有项目名,通过 return 展示在前端界面。具体的前端代码如下:

<h2>展示所有的项目</h2>
{% for p in projects %}
<div>
{{ p }}
<a href="/build?project={{p}}">构建</a>
</div>
{% endfor %}

在页面上点击构建,程序会跳转到 flask 设置好的 /build 这个 url 中,这个路由负责运行自动化测试的代码,他会接收用户传过来的 project 参数,找到在 workspace 目录下的项目,再执行自动化测试指令(这里统一用 pytest 指令)。

@app.route("/build", methods=['get', 'post'])
def build():
project_name = request.args.get('project')
pytest.main([f'workspace/{project_name}'])
return "build success"

到目前为止,完整的流程是这样的:首先,在平台首页会展示所有可以构建的项目,这些项目其实就是把 workspace 子目录当中的目录名列举出来;然后,点击项目旁边的构建按钮,跳转到 /build,根据项目名称执行自动化指令,等待自动化任务执行完成,返回 build success。

3、优化

你基本上已经实现了功能,现在功能测试人员可以通过你搭建的简易平台执行自动化命令。但是这个平台还存在一些问题:第一、没有收集到构建信息,无法查看测试之后的结果;第二、用户必须等待自行测试脚本执行完成,才能返回前端具体的结果,如果自动化测试的执行时间很长,用户会一直停在这个页面,无法做其他事情。

你想到了并发编程,创建一个子进程单独去运行自动化测试脚本,因为子进程可以和主进程独立,所以不需要等待子进程执行完成,主进程就可以立即给前端返回结果。于是你重新编写了 build 函数:

@app.route("/build")
def build():
id = uuid.uuid4().hex
project_name = request.args.get('project')
with open(id, mode='w', encoding='utf-8') as f:
subprocess.Popen(
['pytest', f'workspace/{project_name}'],
stdout=f
)
return redirect(f'/build-history/{id}')

1、首先,通过 uuid 生成一个 id 号来表示这一次构建任务,之后可以通过这个 id 号查看此次构建的记录;

2、通过 subprocess 创建子进程运行自动化任务,把输出结果保存到文件当中,文件名就是生成的 id 号,之后想查看构建的结果时,只需要读取这个文件当中的内容;

3、只要子进程创建成功,马上通过 redirect 重定向到查看结果的 url, 此时并不需要等到子进程执行完就可以查看构建结果。

查看构建结果只需要通过 id 读取文件中的内容返回。

@app.route("/build-history/<id>")
def build_history(id):
with open(id, encoding='utf-8') as f:
data = f.read()
return data

4、生成器

上面读取文件的代码有点问题。当构建重定向到 /build-histrory 后,此时自动化测试脚本才刚刚执行,读取文件中的内容是空的。只有当测试脚本运行,产生越来越多的运行记录,文件中才会出现更多的内容,你必须手动刷新页面才能获取这些新内容。 当自动化任务执行时间很长的时候,你需要不停的刷新 /build-history 页面才能获取最新的构建信息。直到子进程结束,不再有新的内容被写入文件。

为了动态获取文件数据,你使用了生成器惰性获取数据,在 /build-history 的页面加载过程中,只要运行自动化任务的子进程还在运行,就不停的读取文件内容,将它们动态的返回给前端页面。

为了判断子进程的状态,在 /build 的时候,把子进程的 pid 传给 /build-history。

@app.route("/build", methods=['get', 'post'])
def build():
id = uuid.uuid4().hex
project_name = request.args.get('project')
with open(id, mode='w', encoding='utf-8') as f:
proc = subprocess.Popen(
['pytest', f'workspace/{project_name}'],
stdout=f
)
return redirect(f'/build-history/{id}?pid={proc.pid}')

在查看结果时,先编写一个生成器 stream, 每次读取文件中 100 长度的数据,直到进程运行结束。除了通过构建后的重定向,你也可以手动输入 id,查看历史构建记录。此时只需传 id, 不需要传进程名,直接读取文件中的数据。就算文件特别大,也可以通过批量加载,不至于因为同时读取大量数据给服务器造成压力。

import psutil

@app.route("/build-history/<id>")
def build_history(id):
pid = request.args.get('pid', None)
def stream():
f = open(id, encoding='utf-8')
if not pid:
while True:
data = f.read(100)
if not data:
break
yield data
else:
try:
proc = psutil.Process(pid=int(pid))
except:
return 'no such pid'
else:
while proc.is_running():
data = f.read(100)
yield data
return Response(stream())

最后效果:

5、总结

一般来说,做自动化测试只需要做到第一步,有脚本可以执行,就可以代替重复劳动。做测试平台只是让脚本变得更加好用。

但是有很多的测试平台让自动化运行起来更加复杂,要配置很多很多参数才能跑一个完整的测试用例,这似乎有点折本求末,也是很多人都在做的事情。

本文通过 flask 程序实现了一个最简单的 toy jenkins,辅助理解像 jenkins 这样的工具如何执行任务。其实像简单的构建任务,做起来也有很多问题需要解决,这些只有在遇到具体业务的时候我们才会去思考。

希望我们做的工具都是实用的,好用的。

优化自动化测试流程,使用 flask 开发一个 toy jenkins工具的更多相关文章

  1. Django实战总结 - 快速开发一个数据库查询工具

    一.简介 Django 是一个开放源代码的 Web 应用框架,由 Python 写成. Django 只要很少的代码就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 ...

  2. 开发一个 Parcel-vue 脚手架工具

    前言 像我们熟悉的 vue-cli,create-react-app 等脚手架,只需要输入简单的命令 vue init webpack project,即可快速帮我们生成一个初始项目.在实际工作中,我 ...

  3. 开发一个简单的工具,导出github仓库所有issue列表

    Jerry有一个github仓库,专门用来存放自己的知识管理,通过一条条的issue来记录具体的知识点: https://github.com/i042416/KnowlegeRepository/i ...

  4. 使用flask开发网站后端

    Flask 是一个用于 Python 的微型网络开发框架,可以用于快速的搭建一个小型的网站. 我的搜索引擎:http://www.abelkhan.com 就是基于flask开发 一个flask的He ...

  5. 如何开发一个APP——转自知乎

    作者:简单点链接:https://www.zhihu.com/question/22999185/answer/155469014来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  6. flask开发问题小记

    前因 最近在使用flask开发一个APP的后端时出现了一些小问题.我使用sqlalchemy建立了如下多对多关系: 中间表 user_manager_group = db.Table('manage_ ...

  7. 前端每日实战:116# 视频演示如何用 CSS 和原生 JS 开发一个监控网络连接状态的页面

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/oPjWvw 可交互视频 此视频是可 ...

  8. 【Flask系列】开发一个简单的Flask程序

    知识点 初始化:每一个flask程序都必须创建一个程序实例,遵循WSGI(Web Server Gateway interface)协议,把请求->flask Obj; 创建实例: app = ...

  9. 全球首个全流程跨平台界面开发套件,PowerUI分析

    一.       首个全流程跨平台界面开发套件,PowerUI正式发布 UIPower在DirectUI的基础上,自主研发全球首个全流程跨平台界面开发套件PowerUI(PUI)正式发布,PowerU ...

随机推荐

  1. springboot源码解析-管中窥豹系列

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  2. 鸟哥的linux私房菜——第六章学习(Linux文件与目录管理)

    ******************第六章学习****************** 1.[文件与目录管理] 在所有目录下面都会存在的两个目录,分别是 "." 与 "..& ...

  3. 2019南昌网络赛H The Nth Item(二阶线性数列递推 + 广义斐波那契循环节 + 分段打表)题解

    题意: 传送门 已知\(F(n)=3F(n-1)+2F(n-2) \mod 998244353,F(0)=0,F(1)=1\),给出初始的\(n_1\)和询问次数\(q\),设每一次的答案\(a_i= ...

  4. hdu5693D++游戏 区间DP-暴力递归

    主要的收获是..如何优化你递推式里面不必要的决策 之前的代码 这个代码在HDU超时了,这就对了..这个复杂度爆炸.. 但是这个思路非常地耿直..那就是只需要暴力枚举删两个和删三个的情况,于是就非常耿直 ...

  5. 【情人节选帽子】TCS34725颜色传感器和Python图形界面编程(STM32 HAL库)

    截图 描述: l  STM32 HAL库编程 l  使用模拟IIC通信,方便程序移植 l  Python界面编写,蘑菇头的帽子是什么颜色 l  STM32 HAL库串口通信 l  Python界面使用 ...

  6. cheerio & jQuery for server

    cheerio & jQuery for server Fast, flexible & lean implementation of core jQuery designed spe ...

  7. How to enable HTTPS for local development in macOS using Chrome

    How to enable HTTPS for local development in macOS using Chrome HTTPS, macOS, Chrome local HTTPS htt ...

  8. virtual whiteboard

    virtual whiteboard 虚拟白板 / canvas https://twitter.com/excalidraw https://excalidraw.com/ https://gith ...

  9. 微信小程序 TypeScript bug

    微信小程序 TypeScript bug 执行自定义预览前预处理命令失败! internal/modules/cjs/loader.js:584 throw err; ^ Error: Cannot ...

  10. write a node cli tools, step by step

    write a node cli tools, step by step how to write a node cli tools node cli tools, step by step, nod ...