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)


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 火车上空调太冷怎么办 格力空调太冷怎么办 未满月龙猫宝宝怎么办 房间小空调太冷怎么办 奶猫半夜不睡觉怎么办 龙猫不喜欢吃草粒怎么办 龙猫一直在发抖怎么办 龙猫牙齿掉了怎么办 龙猫晚上很吵怎么办 小狗把手咬破了怎么办 夏天仓鼠太热了怎么办 仓鼠玩转轮会摔怎么办 如何发截图不会发现吐怎么办 嘴巴烂了药过敏怎么办 非法校车被扣了怎么办 手指削掉一块肉怎么办 在广州误走brt道怎么办 平安eq测试没过怎么办 物业在小区私自建房怎么办 婚后父母出钱买房离婚怎么办 情人忙工作没时间约会怎么办 情人很忙不主动联系我怎么办 丈夫毫不关心基督徒妻子怎么办 眼睛太小怎么办不整容 被野生猴子咬了怎么办 被野猴子抓伤了怎么办 孩子入学前疫苗补不全怎么办 额头肤色比脸黑怎么办 高跟鞋小了挤脚怎么办 老板欠工人工资不给怎么办 小孩吃了生豆子怎么办 拍夜晚的天空太亮怎么办 购房发票字体不清晰怎么办 接到客人电话预定时怎么办 产检资料弄丢了怎么办 工作中同事喜欢争风头怎么办 想出卖身体赚钱了怎么办 孩子生下来不想要了怎么办 舌头被铁管冻住怎么办 人被水泥冻住了怎么办 姑娘果孕妇吃了怎么办