flask这个框架很轻量,做一些小工具还是可以很快上手的。 1、自动化 某一天你入职了一家高大上的科技公司,开心的做着软件测试的工作,每天点点点,下班就走,晚上陪女朋友玩王者,生活很惬意。 但是美好时光一般不长,这种生活很快被女主管打破。为了提升公司测试效率,公司决定引入自动化流程,你在网上搜了一套技术方案pythonselenium,迅速写了一套自动化测试的脚本。fromseleniumimportwebdriverdeftestselenium():driverwebdriver。Firefox()driver。get(http:www。baidu。com)。。。driver。quit()。。。 编写脚本的日子很累,你需要每天加班,而且没有加班工资。虽然如此,你也没有太多怨言,因为你能明显感觉到自己一点点掌握了自动化测试的流程,正在踏入职业发展的新阶段。这套脚本很快用于公司的主流程测试,也会在回归测试中使用。 因为大量的重复劳动都可以用这套自动化测试脚本代替,于是你又有时间陪女朋友了,上班也可以偶尔划水了,也可以时不时瞄一瞄自己的基金有没有涨。 当然,美好时光一般不长。在一次大改版中,前端页面发生了大量变化,你的自动化测试代码因为没有做抽象封装,基本已经不能用了。 又可以加班了,生活又可以充实起来了。你动用了一些像PageObject的模式对代码进行了重新设计,也加入了关键字驱动,尽量让测试逻辑变成可配置的。设计完成以后,当前端页面变化时,只需要重点维护关键字表格。 你又为公司做了一些贡献,你已经完全胜任自动化测试的工作,甚至能够带一两个小弟。他们时不时找你问一些问题,但是对于自动化的维护工作还是要靠你自己,当你请假时,这些工作只能停滞。于是公司希望你做一些改进,让功能测试人员也可以运行这些自动化测试。 2、开始测试平台 你看到网上有很多人提到测试平台,想着自己也可以做一个可视化平台,这样功能测试人员也可以通过在界面上进行简单的设置,就可以使用底层的自动化代码了。很快flask出现在你的视线中,你做的第一个功能就是实现类似于jenkins的构建功能。 首先,你搭建了一个flask服务,服务启动后,你能顺利访问5000端口。fromflaskimportFlaskappFlask(name)app。run(port5000) 然后,你配置了一个url地址,当访问这个url地址时,服务会调用一个函数,这个url和函数的绑定关系就是路由。函数的返回值可以是普通字符串,可以是json数据,也可以是html页面。app。route()defindex():showallprojectsinworkspacedirworkspacepathlib。Path(app。rootpath)workspaceprojects〔project。nameforprojectinworkspace。iterdir()〕returnrendertemplate(index。html,projectsprojects) 上面的代码就是模仿jenkins,把自动化测试的脚本放在项目的workspace目录下,当访问根路径时,index函数就会被调用。index函数的作用就是列举workspace目录下的所有项目名,通过return展示在前端界面。具体的前端代码如下:h2展示所有的项目h2{forpinprojects}{{p}}构建{endfor} 在页面上点击构建,程序会跳转到flask设置好的build这个url中,这个路由负责运行自动化测试的代码,他会接收用户传过来的project参数,找到在workspace目录下的项目,再执行自动化测试指令(这里统一用pytest指令)。app。route(build,methods〔get,post〕)defbuild():projectnamerequest。args。get(project)pytest。main(〔fworkspace{projectname}〕)returnbuildsuccess 到目前为止,完整的流程是这样的:首先,在平台首页会展示所有可以构建的项目,这些项目其实就是把workspace子目录当中的目录名列举出来;然后,点击项目旁边的构建按钮,跳转到build,根据项目名称执行自动化指令,等待自动化任务执行完成,返回buildsuccess。 3、优化 你基本上已经实现了功能,现在功能测试人员可以通过你搭建的简易平台执行自动化命令。但是这个平台还存在一些问题:第一、没有收集到构建信息,无法查看测试之后的结果;第二、用户必须等待自行测试脚本执行完成,才能返回前端具体的结果,如果自动化测试的执行时间很长,用户会一直停在这个页面,无法做其他事情。 你想到了并发编程,创建一个子进程单独去运行自动化测试脚本,因为子进程可以和主进程独立,所以不需要等待子进程执行完成,主进程就可以立即给前端返回结果。于是你重新编写了build函数:app。route(build)defbuild():iduuid。uuid4()。hexprojectnamerequest。args。get(project)withopen(id,modew,encodingutf8)asf:subprocess。Popen(〔pytest,fworkspace{projectname}〕,stdoutf)returnredirect(fbuildhistory{id}) 1、首先,通过uuid生成一个id号来表示这一次构建任务,之后可以通过这个id号查看此次构建的记录; 2、通过subprocess创建子进程运行自动化任务,把输出结果保存到文件当中,文件名就是生成的id号,之后想查看构建的结果时,只需要读取这个文件当中的内容; 3、只要子进程创建成功,马上通过redirect重定向到查看结果的url,此时并不需要等到子进程执行完就可以查看构建结果。 查看构建结果只需要通过id读取文件中的内容返回。app。route(buildhistoryid)defbuildhistory(id):withopen(id,encodingutf8)asf:dataf。read()returndata 4、生成器 上面读取文件的代码有点问题。当构建重定向到buildhistrory后,此时自动化测试脚本才刚刚执行,读取文件中的内容是空的。只有当测试脚本运行,产生越来越多的运行记录,文件中才会出现更多的内容,你必须手动刷新页面才能获取这些新内容。当自动化任务执行时间很长的时候,你需要不停的刷新buildhistory页面才能获取最新的构建信息。直到子进程结束,不再有新的内容被写入文件。 为了动态获取文件数据,你使用了生成器惰性获取数据,在buildhistory的页面加载过程中,只要运行自动化任务的子进程还在运行,就不停的读取文件内容,将它们动态的返回给前端页面。 为了判断子进程的状态,在build的时候,把子进程的pid传给buildhistory。app。route(build,methods〔get,post〕)defbuild():iduuid。uuid4()。hexprojectnamerequest。args。get(project)withopen(id,modew,encodingutf8)asf:procsubprocess。Popen(〔pytest,fworkspace{projectname}〕,stdoutf)returnredirect(fbuildhistory{id}?pid{proc。pid}) 在查看结果时,先编写一个生成器stream,每次读取文件中100长度的数据,直到进程运行结束。除了通过构建后的重定向,你也可以手动输入id,查看历史构建记录。此时只需传id,不需要传进程名,直接读取文件中的数据。就算文件特别大,也可以通过批量加载,不至于因为同时读取大量数据给服务器造成压力。importpsutilapp。route(buildhistoryid)defbuildhistory(id):pidrequest。args。get(pid,None)defstream():fopen(id,encodingutf8)ifnotpid:whileTrue:dataf。read(100)ifnotdata:breakyielddataelse:try:procpsutil。Process(pidint(pid))except:returnnosuchpidelse:whileproc。isrunning():dataf。read(100)yielddatareturnResponse(stream()) 最后效果: 5、总结 一般来说,做自动化测试只需要做到第一步,有脚本可以执行,就可以代替重复劳动。做测试平台只是让脚本变得更加好用。 但是有很多的测试平台让自动化运行起来更加复杂,要配置很多很多参数才能跑一个完整的测试用例,这似乎有点折本求末,也是很多人都在做的事情。 本文通过flask程序实现了一个最简单的toyjenkins,辅助理解像jenkins这样的工具如何执行任务。其实像简单的构建任务,做起来也有很多问题需要解决,这些只有在遇到具体业务的时候我们才会去思考。 希望我们做的工具都是实用的,好用的。 文章来自https:www。cnblogs。comheniup16565781。html