ubuntu下几种Android测试工具
来源:互联网 发布:uiautomator python 编辑:程序博客网 时间:2024/06/05 07:44
前段时间导师要对中国app市场(我们选的是百度,其实哪家都一样)上app的通信安全(登录的时候,用户名密码有没有加密)做自动化检测。
解决的方法就是在emulator里自动登录app,然后用mitmproxy抓捕数据,对数据进行分析。(怎么分析以后再讲,今天先写操控emulator的工具,图片就不放了,反正也没啥人看)。下面都是自己的一家之言,如果有什么摸索的不对的,欢迎指正。
第一个是google的hierarchyviewer。
hierarchyviewer的优点:
1.给出手机(以下不提及都是只phone或者emulatoer都可以)当前显示屏幕的UI结构。会用一棵树的形式表现出来。你选择不同的节点,会在hierarchyviewer右下角的分区显示这个节点对应屏幕上的哪一块。
2.另外有个模式,能定位你鼠标所指位置的像素坐标。我觉得这个可能对美工很有用吧,没接触过美工的工具,但是这个能自动放大你鼠标所在位置的图片,像素点很明显的一个个方块。
hierarchyviewer的缺点:
1.很卡,我笔记本是外星人的,还是很卡,用起来很不爽。
2.也就是让你看看UI结构,获得信息并不多。也不能对手机做出操作。
第二个是monkeyrunner。
monkeyrunner是Jython(用JAVA实现的python,它是一个Python语言在Java中的完全实现)。但是我们完整的自动检测程序都打算用python来写,所以这个用用也就放弃了,因为我们找到了可以替代它的,python实现的工具。(下面就会讲)。
不过monkeyrunner可以应付很多安卓测试了吧(没有在公司干过,不知道测试岗具体要做啥)。点击,滑动,输入,截屏,monkeyrunner都可以实现。
这是官方文档的地址:http://developer.android.com/tools/help/MonkeyDevice.html
然后有个中文版的小例子,基本功能也有介绍了:http://fengbohaishang.blog.51cto.com/5106297/962705
第三个是AndroidViewClient
dtmilano大神用python做的,个人觉得比monkeyrunner更强大好用。在我们自己写工具前,主要用的就是它啦。一个python实现的强大的moudle。你可以把它下载下来,在你的代码里作为import。https://github.com/dtmilano/AndroidViewClient
不过它有个问题,UiAutomator limitation。你在使用dump获取当前UI信息的时候,如果太频繁就会崩。所以师兄后来看了androidviewclient的源码,我们自己写了我们需要的库。
第四个我们自己系的代码
这里我就直接贴代码了。名字起的都是人能看懂的,所以没怎么注释。
import subprocess # class Deivceimport timeimport xml.etree.ElementTree as ETclass View(object): def __init__(self, d): ''' initalize with a dict obj scrollable : bool, text: str long_clickable : bool, focused: bool checkable: bool, clickable: bool, password: bool, classtype: (class)str e.g. TextView index: int, checked: bool package: str e.g.com.qiyi.video, selected: bool enabled: bool, bounds: ((x1,y1),(x2,y2)) content_desc: str, resource_id: str e.g. com.qiyi.video:id/xxx focusable: bool, naf: (NAF)(optional) bool center: (x1, y1) center of bounds ''' self.scrollable = True if d.get('scrollable')=='true' else False self.text = d.get('text') self.long_clickable = True if d.get('long-clickable')=='true' else False self.focused = True if d.get('focused')=='true' else False self.checkable = True if d.get('checkable')=='true' else False self.clickable = True if d.get('clickable')=='true' else False self.password = True if d.get('password')=='true' else False self.classtype = (d.get('class')).replace('android.widget.','') self.index = int(d.get('index')) self.checked = True if d.get('checked')=='true' else False self.package = d.get('package') self.selected = True if d.get('selected')=='true' else False self.enabled = True if d.get('enabled')=='true' else False tmp = tuple((d.get('bounds')).strip('[]').split('][')) # ('1,2','3,4') pos1, pos2 = tmp[0].split(','), tmp[1].split(',') # ['1','2'] self.bounds = (int(pos1[0]),int(pos1[1])), (int(pos2[0]),int(pos2[1])) self.content_desc = d.get('content-desc') self.resource_id = d.get('resource-id') self.focusable = True if d.get('focusable')=='true' else False self.naf = True if d.get('NAF')=='true' else False (x1, y1), (x2, y2) = self.bounds self.center = x1+(x2-x1)/2, y1+(y2-y1)/2def getDefaultConfig(): import configparser cfg = configparser.SafeConfigParser() cfg.read('config.cfg') dtype=cfg.get('device','type') height = cfg.get(dtype, 'height') width = cfg.get(dtype, 'width') go2appspos = tuple((cfg.get(dtype, 'go2appspos')).split(',')) appspos = tuple((cfg.get(dtype, 'appspos')).split(',')) welcomepos = tuple((cfg.get(dtype, 'welcomepos')).split(',')) default_apps = (cfg.get(dtype, 'apps')).split(',') default_pkgs = (cfg.get(dtype, 'pkg')).split(',') config = { 'height':height, 'width':width, 'go2appspos':go2appspos, 'appspos':appspos, 'welcomepos':welcomepos, 'default_apps':default_apps, 'default_pkgs':default_pkgs } return configclass Device(object): def __init__(self, sno, config=getDefaultConfig()): ''' ''' self.sno = sno self.height =int(config['height']) self.width = int(config['width']) self.welcomepos = config['welcomepos'] self.go2appspos = config['go2appspos'] self.appspos=config['appspos'] self.default_apps = config['default_apps'] self.default_pkgs = config['default_pkgs'] self.adbshellpf = 'adb -s '+self.sno+' shell ' self.adbpf = 'adb -s '+self.sno+' ' @staticmethod def shell(cmd, runtime): res = subprocess.check_output(cmd, shell=True, universal_newlines=True, timeout=runtime) return res def parseDump2vl(self, fpath): '''parse dump to view-list([view,...])''' tree = ET.parse(fpath) vl = [] for e in tree.iter(): if e.tag == 'hierarchy': continue vl.append(View(e.attrib)) return vl def dump(self, filepath='', savefile=False): '''get list of view from current ui return [ view, ...]''' if filepath == '': fpath = '/tmp/trafficgen/'+self.sno+'/uidump.xml' else: fpath = filepath t = 1 for chance in range(6): time.sleep(t-1) # '-1': no wait for first attempt of dump try: cmd = self.adbshellpf+' uiautomator dump --compressed' self.shell(cmd, 10) cmd = self.adbpf+' pull /storage/sdcard/window_dump.xml '+fpath self.shell(cmd, 10) except: if(chance == 5): if not savefile: cmd = 'rm '+fpath self.shell(cmd, 10) print('dump failed') raise t *= 2 else: break finally: cmd = self.adbshellpf+' rm /storage/sdcard/window_dump.xml' self.shell(cmd, 10) vl = self.parseDump2vl(fpath)#default: /tmp/trafficgen/sno/dumpui.xml if not savefile: cmd = 'rm '+fpath self.shell(cmd, 10) return vl def tap(self, pos, waittime=0): '''pos: (x, y), sleeptime: sleep time''' x, y = pos cmd = self.adbshellpf+' input tap '+str(x)+' '+str(y) self.shell(cmd, 10) if waittime > 0: time.sleep(waittime) def swipe(self, startpos, endpos, duration=512): '''startpos, endpos : (x1,y1),(x2,y2)''' x1, y1 = startpos x2, y2 = endpos x1,y1,x2,y2,duration = str(x1),str(y1),str(x2),str(y2),str(duration) cmd = self.adbshellpf+' input swipe '+' '.join((x1,y1,x2,y2,duration)) self.shell(cmd, 10) def swipeLeft(self): margin = self.width/10 w,h = self.width, self.height startpos, endpos = (w-margin, h/2),(margin, h/2) self.swipe(startpos, endpos, 200) def swipeRight(self): margin = self.width/10 w,h = self.width, self.height endpos, startpos = (w-margin, h/2),(margin, h/2) self.swipe(startpos, endpos, 200) def swipeUp(self): margin = self.height/5 w,h = self.width, self.height startpos, endpos = (w/2, h - margin), (w/2, margin) self.swipe(startpos, endpos, 200) def swipeDown(self): margin = self.height/5 w,h = self.width, self.height endpos, startpos = (w/2, h - margin), (w/2, margin) self.swipe(startpos, endpos, 200) def inputText(self, msg): ''' adb shell input text msg msg only accept [0-9][a-zA-Z]|@|. space no supported ''' cmd = self.adbshellpf+' input text '+msg self.shell(cmd, 10) def screencap(self, filepath='.'): ''' adb shell screencap /storage/sdcard/sc.png adb pull /storage/sdcard/sc.png filepath filepath: local file path e.g. ./dir/screen.png ''' remotefile = '/storage/sdcard/sc.png' cmd = self.adbshellpf+' screencap '+remotefile self.shell(cmd, 10) cmd = self.adbpf+' pull '+remotefile+' '+filepath self.shell(cmd, 10) def go2home(self): """input keyevent KEYCODE_HOME""" cmd = self.adbshellpf+' input keyevent KEYCODE_HOME' self.shell(cmd, 10) time.sleep(1) def go2apps(self): self.tap(self.go2appspos, 1) def installApp(self, fpath): cmd = self.adbpf + ' install '+ fpath self.shell(cmd, 60) def getOne3rdPartyApp(self): """return 1 user-installed 3rd party package return: None | pkg name""" cmd = self.adbshellpf + " pm list packages -3 " tmp_res = self.shell(cmd, 30) tmp_res = tmp_res.split('\r\n') for line in list(tmp_res): # x86 img contain WARNING msg if ('WARNING' in line) or ('' == line): tmp_res.remove(line) res = None for entry in tmp_res: pkg = (entry.split(':'))[1] if pkg not in self.default_pkgs: res = pkg return res def uninstallApp(self): """uninstall the only package pkg do nothing | remove the pkg""" pkg_name = self.getOne3rdPartyApp() if(pkg_name == None): return False cmd = self.adbpf + " uninstall " + pkg_name self.shell(cmd, 60) return True def startApp(self): '''precondition: at Apps page start new installed app''' flag = False self.tap(self.appspos, 1) self.swipeRight() for pageindex in range(2): vl = self.dump() for v in vl: if ('TextView' == v.classtype) and \ (v.text not in self.default_apps): flag = True self.tap(v.center, 1) return flag self.swipeLeft() return flag def forcestopApp(self): """force stop app by pkg-name adb shell am force-stop pkg-name""" flag = False pkg = self.getOne3rdPartyApp() if pkg == None: return flag cmd = self.adbshellpf + " am force-stop " + pkg self.shell(cmd, 10) flag = True return flag def cleanupSdcard(self): ''' rm -r /storage/sdcard/* mkdir /storage/sdcard/D1 ... ''' sdir = '/storage/sdcard/' dirs1 = ['Alarms','DCIM','Download','LOST.DIR'] dirs2 = ['Movies','Music','Notifications'] dirs3 = ['Pictures','Podcasts','Ringtones'] dirs = dirs1+dirs2+dirs3 cmd = '; mkdir ' for d in dirs: d = sdir+d+' ' cmd += d cmd = ' rm -r '+sdir+'* '+cmd cmd = self.adbshellpf+" '"+cmd+"'" self.shell(cmd, 10) def skipWelcomeMsg(self): self.tap(self.welcomepos, 2) self.go2apps() self.tap(self.welcomepos, 5) self.go2home()def startadbserv(): cmd = 'adb start-server' subprocess.check_output(cmd, shell=True, timeout=30)def stopadbserv(): cmd = 'adb kill-server' subprocess.check_output(cmd, shell=True, timeout=30) if __name__=='__main__': sno = 'emulator-5554' device = Device(sno)
- ubuntu下几种Android测试工具
- Android 测试工具------ monkeyrunner工具
- Android 性能测试工具
- Android自动化测试工具
- Android 自动化测试工具
- Android自动化测试工具
- Android测试工具小结
- Android测试工具小结
- android测试工具
- android 自动化测试工具
- 【android】app测试工具
- Android测试工具小结
- Android测试工具小结
- Android 测试工具Monkey
- android的测试工具
- Android Monkey测试工具
- android性能测试工具
- android性能测试工具
- android studio的安装
- Java字符串总结
- 退款流程
- iOS通过dSYM文件分析crash
- 对账流程
- ubuntu下几种Android测试工具
- 实现函数功能对数组元素进行插入、删除、查询操作
- 入门指南
- 记账流程
- 清算流程
- Why choose Lua?
- OJ第三批——Problem N: 熟悉题型——填空题(删除线性表节点)
- 触摸模式(Touch Mode)
- c++面试题