文章首发:我的博客

背景

最近在测试安卓的时候,经常会用到Monkey,Monkey作为安卓的基础工具,必须要到命令行去敲敲敲,做起来非常非常麻烦,于是我就想能不能利用学会的Python知识直接开发一个带有界面的安卓测试工具。

思路

整个实现流程并不困难,一个界面填写数据,提交到后台,然后Python调用os模块,执行shell命令,然后就可以实现GUI端的Monkey了。

Web端

前段时间一直都在看web端的知识,我就想用web写一个界面,然后后台用Django执行,就可以很简单的完成这个工具了。

使用POST表单实现

前端就是用Bootstrap来实现了,对我来说,写了这么久得博客,这块东西比较容易了,代码就不放了,bootstrap套套就出来了,界面图如下:

本来上面还有一个结束Monkey按钮的,被我拿掉了,拿掉的原因后面会说。

现在该有的东西都有了,后台实现起来也不难,获取前端给的参数,然后执行命令就行了,第一版用的是POST提交表单的方式执行,我发现了这么些问题:

  1. 点击执行Monkey命令的时候整个页面是卡住的,没办法点击获取Monkey进程和结束Monkey
  2. 执行完Monkey后整个页面会刷新,也就是说没办法从前端获取我命令的参数,虽然日志中可以获取到命令相关的参数,但是总的来说并不是那么直观

使用Ajax提交表单

用POST提交是最简单的了,我也最熟悉,不过存在上面的问题,总是很操蛋,于是我就改为使用Ajax异步提交数据,这样就不会导致执行Monkey的时候整个页面卡死的情况了。

页面没有修改,改了一个方法,jQuery的方法如下:

$(document).ready(function () {$("#sub").click(function () {$.post("/monkey/",{"package_name": $("#package_name").val(),"event_count": $("#event_count").val(),"log_path": $("#log_path").val(),"log_level": $("#log_level").val(),"delay": $("#delay").val(),"sys_event": $("#sys_event").val(),"touch_event": $("#touch_event").val(),"zoom_event": $("#zoom_event").val(),"finger_event": $("#finger_event").val(),"ball_event": $("#ball_event").val(),"nav_event": $("#nav_event").val(),"switch_event": $("#switch_event").val()})})
});

使用这个方法实现,是解决了一些问题,执行Monkey的时候页面也不会卡了,也可以正常的获取Monkey进程,不过依然存在这么些问题:

  1. 端口容易出现异常,我执行的过程中发现Monkey经常会占用两个pid
  2. 执行停止Monkey命令的时候无法生效

第一个问题倒是还好,占用两个结束两次就行了,倒也还能接受,第二个问题就无法接受了,NND不能直观的开始和结束,我写这个工具就没有什么意义了,我专门试了直接调用python执行停止命令是正常的。代码如下:

$(document).ready(function () {$("#stop_monkey").click(function () {$.post("/monkey/stop_monkey/",{"monkey_id": $("#monkey_id").val()})})
});
@csrf_exempt
def stop_monkey(request):data = {"msg": "Monkey已经停止"}if request.method == 'POST':command = 'adb shell kill {}'.format(request.POST['monkey_id'])cm.run_monkey(command)return JsonResponse(data, safe=False)return JsonResponse(data, safe=False)

若读者知道,请发邮件告诉我到底是为什么这里不会执行,我丢了print在这上面是可以正常打印的。

GUI端

web端的实验失败了,效果并没有达到我的预期,只能换一种方案了,用桌面的GUI端来实现这个方案试试,然而GUI端又有很多选择,PYQT据说是最好的,但是在我电脑数据抹掉之前我有装过这个,虽然最后装成功了,但是整个装的过程太操蛋了,有点心理阴影,wxpython也是一个不错的选择,不过还要装第三方包,有点折腾,最终我选择了最基本的Tkinter来处理GUI。那么问题来了,Tkinter没用过,要重新去熟悉这个包。

《Tkinter介绍文档》。。。麻蛋又是全英文的,果然英语还是必须要学好,我大概看了两小时,把一些主要的点扫了一遍,就开始写了,代码丑也是没办法的,时间少,没办法去慢慢想设计模式。

写完的界面是这样的:

按钮方法

整个GUI的代码有100多行,有点长,布局是用Grid写的,所有的代码我就不贴了,我把按钮的方法贴一下:

connect_text = Button(master, text='获取设备号',command=lambda: cm.set_text(device_name, ad.get_devices()))
up_pknm_conf = Button(master, text='修改包名',command=lambda: cm.update_conf(status, 'package_name', cm.get_text(package_name)))
up_log_path_conf = Button(master, text='修改日志地址',command=lambda: cm.update_conf(status, 'log_path', cm.get_text(log_path)))
up_log_level_conf = Button(master, text='修改日志等级',command=lambda: cm.update_conf(status, 'log_level', cm.get_text(log_level)))
up_count_conf = Button(master, text='修改测试数量',command=lambda: cm.update_conf(status, 'count', cm.get_text(count)))
up_delay_conf = Button(master, text='修改延时',command=lambda: cm.update_conf(status, 'delay', cm.get_text(delay)))
up_touch_conf = Button(master, text='修改触摸事件',command=lambda: cm.update_conf(status, 'touch', cm.get_text(touch)))
up_motion_conf = Button(master, text='修改手势事件',command=lambda: cm.update_conf(status, 'motion', cm.get_text(motion)))
up_pinch_conf = Button(master, text='修改缩放事件',command=lambda: cm.update_conf(status, 'pinch', cm.get_text(pinch)))
up_trackball_conf = Button(master, text='修改轨迹球事件',command=lambda: cm.update_conf(status, 'trackball', cm.get_text(trackball)))
up_screen_conf = Button(master, text='修改屏幕事件',command=lambda: cm.update_conf(status, 'screen', cm.get_text(screen)))
up_nav_conf = Button(master, text='修改导航事件',command=lambda: cm.update_conf(status, 'nav', cm.get_text(nav)))
up_major_conf = Button(master, text='修改主要事件',command=lambda: cm.update_conf(status, 'major', cm.get_text(major)))
up_system_conf = Button(master, text='修改系统事件',command=lambda: cm.update_conf(status, 'system', cm.get_text(system)))
up_app_conf = Button(master, text='修改切屏事件',command=lambda: cm.update_conf(status, 'app', cm.get_text(app)))
up_keyboard_conf = Button(master, text='修改键盘事件',command=lambda: cm.update_conf(status, 'keyboard', cm.get_text(keyboard)))
up_anyevents_conf = Button(master, text='修改其他事件',command=lambda: cm.update_conf(status, 'anyevents', cm.get_text(anyevents)))
cat_monkey_pid = Button(master, text='显示Monkey进程',command=lambda: cm.set_text(monkey_pid, ad.get_monkey_id()))
start_monkey = Button(master, text='开始Monkey',command=lambda: mk.merge_command(cm.get_text(log_path), *cm.collect(*ENTRYLIST)))
stop_monkey = Button(master, text='结束Monkey',command=lambda: ad.stop_monkey(status))
get_monkey = Button(master, text='获取Monkey',command=lambda: cm.set_text(status, mk.get_monkey(cm.get_text(log_path), *cm.collect(*ENTRYLIST))))

值得一提的是,在Button组件中的command执行函数是不能带参数的,否则就会报错,不知道Tkinter的作者是怎么想的,不带参数的函数能有几个啊。。。。。

我用了一个折中的方法,用lambda函数来处理这一块,这样就可以加上参数了。

封装其他方法

其他方法我使用了三个函数,common来处理普通的方法,adb专门处理操作shell的方法,而monkey专门处理和Monkey有关的方法,这样设计以后工具就很容易拓展了,可以加上性能监控的方法等高级功能。

简单的贴一下方法。

class Adb:def __init__(self):self.mk = monkey.Monkey()self.cm = common.Common()def get_devices(self):"""获取设备名称:return:设备名称"""a = os.popen('adb devices')devices = a.readlines()spl = devices[1].find(' ')devices_name = devices[1][:spl]if devices_name == '':return "请确认设备是否连接"else:return devices_namedef get_monkey_id(self):"""获取monkey进程ID:return:monkey进程id"""if self.get_devices():a = os.popen('adb shell ps | grep monkey')try:monkey_id = a.read().split(' ')[5]print "进程为{} 的Monkey已停止".format(monkey_id)except Exception:monkey_id = ''return monkey_idelse:print "设备未连接"def stop_monkey(self, entry):"""停止monkey:param monkey_id:monkey的进程号:return:None"""monkey_id = self.get_monkey_id()if monkey_id != '请确认你的设备是否连接':os.system('adb shell kill {}'.format(monkey_id))self.cm.set_text(entry, "进程为{} 的Monkey已停止".format(monkey_id))else:print "设备未连接"
def merge_command(self, path, *args):"""组合命令,Monkey使用:param path:日志地址:param args:Monkey命令中的其他参数:return:None"""member = ' '.join(args)command = 'adb shell monkey {} > {}'.format(member, path)self.run(command)def get_monkey(self, path, *args):"""获取Monkey命令:param path: 日志地址:param args: Monkey命令中的其他参数:return:"""if self.check_total(*args):member = ' '.join(args)command = 'adb shell monkey {} > {}'.format(member, path)return commandelse:return '事件百分数大于100%,请修正后再获取'def check_total(self, *args):"""检查事件百分比是否合规,大于100则返回False:param args:传入的事件列表:return:True"""rst = []all_list = self.deal_list(*args)for x in all_list:x = x.split(' ')[1]rst.append(int(x))num = sum(rst)if num > 100:return Falseelse:return Truedef deal_list(self, *args):"""处理列表数据,返回只有事件的列表:param args:传入的列表数据:return:list"""rst = []for x in range(1, 12):rst.append(args[x])print rstreturn rst

存在的问题

用GUI端来处理也会存在这么个问题,点击开始的时候回出现整个GUI卡住的情况,必须要等Monkey执行完毕才能正常操作其他东西,因此我想了一个折中的方案,加一个状态显示的文本框,加一个生成Monkey命令的按钮,这样生成命令然后在GUI上就可以使用其他命令了,加上多进程或者多线程应该能解决这个问题,但是现在我对多线程多进程几乎是0了解,等了解了之后再来完善这部分的功能。

最后

就实现了这么一点点功能,代码量差不多也要300行,不得不说真是一个操蛋的工程。

最后,我仍然没有解决这个操蛋的问题,等研究完多线程之后再来重新改善这段代码。

最后的最后,虽然功能还不齐全,还是放上Github,怎么说也是一个可以试想功能的工具吧。

最近博客上线了,读者可以关注我的博客。

博客地址:www.wengyb.com

Python开发测试工具(一)—Monkey相关推荐

  1. 3年自动化测试,我突然想转测试开发了,开发测试工具平台......

    目录:导读 前言 一.掌握一门编码语言 二.开发什么样的工具(平台) 三.如何开发测试工具 四.工具的推广与升华 五.总结 前言 那就是刚进入这个行业的时候,可以不用急于去专研某个领域.白岩松老师曾说 ...

  2. Python渗透测试工具合集及书籍推荐(转)

    Python渗透测试工具合集 如果你热爱漏洞研究.逆向工程或者渗透测试,我强烈推荐你使用 Python 作为编程语言.它包含大量实用的库和工具,本文会列举其中部分精华. 本文转自: http://ww ...

  3. Python安全测试工具合集

    Python安全测试工具合集 转自:http://netsecurity.51cto.com/art/201311/417021.htm ------------------------------- ...

  4. 移动开发测试工具——Bugtags的集成

    移动开发测试工具--Bugtags 官网:https://bugtags.com/ 注册开发者账号 注册账号并激活邮箱,都会就不多做介绍了. 创建应用 创建完账号以后会提示添加应用,点击添加 添加应用 ...

  5. 第一百二十五期:程序员的自我救赎,使用Python开发性格分析工具

    如此不均衡的贫富差距,各行业的领导者如何能管理好公司,让员工们既努力产出,又能安于现状呢?每个领导者必学的一门课程就是职场心理学.只有你充分了解员工心理与对应的行为表现,才能从容的掌控各类型的人员,从 ...

  6. messagebox 全部使用_商业篇 | 使用python开发性格分析工具卖钱

    帕累托法则 上世纪初,意大利经济学家维尔弗雷多▪帕累托发现了一个有趣的现象:在意大利, 大约80%的财富掌握在大约20%的人手中,这在后来被概括为帕累托法则(80/20法则),即二八法则.而全球财富报 ...

  7. java开发测试工具

    JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework).Junit测试是程序员测试,即所谓白盒测试,因为程序 ...

  8. python行为驱动测试开发_行为驱动开发在 Python 开发测试中的应用

    行为驱动开发 (BDD) 简介 行为驱动开发是什么? 说到行为驱动开发(BDD),无可避免的要提到敏捷里面的测试驱动开发(TDD),TDD 的主要思想是"代码即文档",其倡导的流程 ...

  9. 程序员的自我救赎,使用python开发性格分析工具

    自我救赎 帕累托法则 上世纪初,意大利经济学家维尔弗雷多▪帕累托发现了一个有趣的现象: 在意大利, 大约80%的财富掌握在大约20%的人手中,这在后来被概括为帕累托法则(80/20法则),即二八法则. ...

最新文章

  1. Spring Boot + Vue + Shiro 实现前后端分离、权限控制
  2. 漫画:5分钟弄懂分治算法!它和递归算法的关系!
  3. arial字体可以商用吗_每次做PPT都不知该怎么选字体?6种万能字体搭配组合送你...
  4. 浅谈Java内存泄漏问题
  5. 主进程中发生javascript错误_你知道 JavaScript 中的错误对象有哪些类型吗?
  6. 实体框架提供程序类型无法加载?
  7. Pytorch BatchNorm
  8. 互联网光环下的新金融群像:运营最重要的是说人话
  9. 货币转换python代码_[Python3 练习] 003 货币转换
  10. 常用电子产品行业标准及认证
  11. GitHub服务中断24小时11分钟事故分析报告
  12. 在 uniapp 中使用阿里图标
  13. python朋友圈点赞统计_微信公众号所有历史文章的标题/点赞数/阅读数统计
  14. 三相并联功率因数校正matlab,基于并联技术的三相功率因数校正方法研究
  15. java获取虎牙直播弹幕消息,虎牙直播弹幕筛选器
  16. java堆是什么意思_java中的“堆栈”是什么意思?
  17. java基于springboot+vue+elementui的外卖点餐配送系统 含骑手功能
  18. redhat 7中DNS 服务器配置与测试
  19. mysql查询所有图书信息_PHP+MySQL使用mysql_num_rows实现模糊查询图书信息功能
  20. lg kv510 java_为妇女节献礼 精美天翼手机 LG KV510评测

热门文章

  1. android开发:android获取IMEI、MSISDN、ICCID、IMSI
  2. 亲手实践图片木马之捆绑工具(BD2.Net Injector By BD2)
  3. 【云原生生态圈】:Docker核心技术全面总结
  4. squirrel sql client linux,SQuirreL SQL Client
  5. 【2020-10-29】记一次WebSocket握手验证反爬虫
  6. html测验               --(w3cshool)
  7. 南冻北旱考验农产品市场 须防游资借机炒作
  8. 【Android】之【App启动】
  9. 在Firefox国际版使用中国版账户(火狐通行证)傻瓜解决办法
  10. AndroidAudioConverter,音频格式无法转换问题