Appium(一):appium环境以及基础函数的封装

一:搭建appium环境

1、JDK和Android SDK配置
2、安装Node.js
3、安装.net framework
4、安装Appium
5、安装appium - python - client

google教程很多,可以参照网上的教程,这里就不赘述了。最后使用appium-doctor检测环境搭建是否ok。
这篇文章主要介绍下基础函数的封装。

二:让appium跑起来

打开appium-server(我这里版本是V1.10.0),点击:Start Server:


1.png

进到如下这个界面,然后点击页面右上角的放大镜:


3.png

进到appium-server的基础信息设置界面,点击Saved Capability Sets:
2.png

这里面可以配置物理机或者模拟器的设备信息,然后开始一个session,需要配置的信息如下:

capabilities = {
    "platformName": "Android",
    "deviceName": "0acfd8d5",
    "appActivity": "com.imooc.component.imoocmain.splash.MCSplashActivity",
    "appPackage": "cn.com.open.mooc",
    "noReset": "true"#在当前 session 下不会重置应用的状态。默认值为 false,设置为true后,不需要每次运行用例都安装apk(特别是有开屏页的app,不需要每次都经过开屏页,打开app直接进入主页)
    }

根据appium版本不同,可能有些参数不是必须的,但是能加上就加上,避免出现问题。其中deviceName可以通过在cmd(windows)窗口下执行adb devices 获取,前提是要先打开设备的调试模式以及windows电脑需要具备adb环境;appActivity和appPackage的获取有很多种方法获取,这里有两种情况:第一种是本地有apk包的情况下,那么直接使用命令aapt dump badging XXX.apk(后面是apk包的绝对路径);第二种情况是没有apk包,但是模拟器或者真机上安装了应用,这时候可以使用adb logcat >c:\log.txt,把app的启动日志输出到本地的log文件,然后去log文件过滤查找,如何过滤查找关键字,这个百度也有,我一般是过滤START或者cmp关键字,后面一般跟的就是apk的Activity和Package名称(不推荐使用这个方法,因为我在多次尝试的过程中遇到华为手机有一个坑爹的问题,就是系统日志默认是关闭的,需要输入一串命令来打开log开关:*#*#2846579#*#*,进入projectmenu--后台设置--LOG设置--LOG开关--打开,最终以失败告终);或者使用adb logcat | grep START命令(windows使用findstr过滤),在cmd窗口输入这个命令后,启动app,然后查看cmd窗口输出的日志


4.png

可以看到输出的日志中,有一个LAUNCHER关键字,后面紧接着flg=0x10200000cmp=cn.com.open.mooc/com.imooc.component.imoocmain.splash.MCSplashActivity中的cn.com.open.mooc就是包名,com.imooc.component.imoocmain.splash.MCSplashActivity就是启动页的Activity(推荐使用这种方法)。
准备工作做完之后,在appium-server的基础信息设置界面选择一个自己已保存的设备,然后点击start session,就可以看到appium-server端有日志输出,这个日志很重要,后续可以作为调试信息来定位问题,同时也能看到手机也就运行起了相应的apk(整个过程都是需要打开调试模式的)

三、页面滑动初级使用

appium的滑动函数是:driver.swipe(x,y,x1,y1,time)。起始点:x,y;终点x1,y1;time是可选参数,左右滑动Y坐标不变,X变化;上下滑动X坐标不变,Y变化。appium中的坐标和数学的坐标不同,appium坐标原点在界面左上角。
滑动函数的封装:
代码和数据分离可以有效减少由于数据变化而维护代码的成本,所以封装一个获取手机屏幕大小和滑动的类,达到的效果是:只需要告诉程序我想要滑动的方向即可,滑动坐标的起始点并不需要关心。
代码实现:

from appium import webdriver
from time import sleep
capabilities = {
    "platformName": "Android",
    #"automationName":"Uiautomator2",#获取toast元素需要的
    "deviceName": "0acfd8d5",
    "appActivity": "com.imooc.component.imoocmain.splash.MCSplashActivity",
    # "appWaitActivity":"cn.com.open.mooc.index.splash.GuideActivity"#真机启动页等待
    "appPackage": "cn.com.open.mooc",
    "noReset": "true"#在当前 session 下不会重置应用的状态。默认值为 false,设置为true后,不需要每次运行用例都安装apk
    }

class getSizeAndSwipe():
    def __init__(self):#构造函数,实例化类的对象时调用
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", capabilities)
        self.size = self.driver.get_window_size()#获取屏幕的大小,返回的形式是{'width': 1080, 'height': 1812}
        self.width = self.size["width"]#获取宽
        self.height = self.size["height"]#获取高

    def swipeUp(self):
        x1 = self.width / 2
        y1 = self.height / 10 * 9#获取高的十分之九的位置
        x2 = x1
        y2 = self.height / 10 * 1#获取十分之一的位置
        self.driver.swipe(x1,y1,x2,y2)#向上滑动x坐标不变,y坐标变小
    
    def swipeDown(self):
        x1 = self.width / 2
        y1 = self.height / 10 * 1
        x2 = x1
        y2 = self.height / 10 * 9
        self.driver.swipe(x1, y1, x2, y2)
    
    def swipeRight(self):
        x1 = self.width / 10 * 1
        y1 = self.height / 2
        x2 = self.width / 10 * 9
        y2 = y1
        self.driver.swipe(x1, y1, x2, y2)
    
    def swipeLeft(self):
        x1 = self.width / 10 * 9
        y1 = self.height / 2
        x2 = self.width / 10 * 1
        y2 = y1
        self.driver.swipe(x1, y1, x2, y2)
    
    def swipeAction(self,direction):#定义一个滑动函数,传入一个方向即可
        if direction == "up":
            self.swipeUp()
        elif direction == "down":
            self.swipeDown()
        elif direction == "left":
            self.swipeLeft()
        else:
            self.swipeRight()
if __name__ == "__main__":
    vivoSwipe = getSizeAndSwipe()
    vivoSwipe.swipeAction("up")#告知滑动的方向即可

四、元素定位

1、id定位
2、class_name定位
3、层级定位
4、uiautomator定位
5、Xpath定位
6、原生页面和webview页面切换,以及toast元素获取

封装一个登陆页面元素定位的类,每种定位方式封装为一个方法,代码实现:

from appium import webdriver
from time import sleep
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
capabilities = {
    "platformName": "Android",
    "automationName":"Uiautomator2",#获取toast元素需要的
    "deviceName": "0acfd8d5",
    "appActivity": "com.imooc.component.imoocmain.splash.MCSplashActivity",
    # "appWaitActivity":"cn.com.open.mooc.index.splash.GuideActivity"#真机启动页等待
    "appPackage": "cn.com.open.mooc",
    "noReset": "true"#在当前 session 下不会重置应用的状态。默认值为 false,设置为true后,不需要每次运行用例都安装apk
    }

class handleLoginElement():
    def __init__(self):
        self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",capabilities)#初始化driver

    def goLoginById(self):#id定位的方式登陆
        bottoms = self.driver.find_elements_by_id("cn.com.open.mooc:id/lav")#底部的四个按钮(首页,手记,我的学习,账号的id属性是一样的,先找出所有的,然后再通过索引查找需要的元素)
        count = bottoms[3]#账号元素的索引为3
        count.click()#做一个点击操作
        sleep(1)
        self.driver.find_element_by_id("cn.com.open.mooc:id/rl_login_before").click()
        self.driver.find_element_by_id("cn.com.open.mooc:id/right_text").click()#若是已有账号,那么直接点击登陆跳转到登陆界面;非首次登陆,不需要这一步,点击登陆会直接跳转到登陆界面
        self.driver.find_element_by_id("cn.com.open.mooc:id/accountEdit").clear()#先清理一下默认的上次输入过的文本
        self.driver.find_element_by_id("cn.com.open.mooc:id/accountEdit").send_keys("15555555555")#输入
        self.driver.find_element_by_id("cn.com.open.mooc:id/passwordEdit").clear()
        self.driver.find_element_by_id("cn.com.open.mooc:id/passwordEdit").send_keys("XXXX")
        self.driver.find_element_by_id("cn.com.open.mooc:id/loginLabel").click()#点击登陆
        sleep(2)

    def goLoginByClassName(self):#name定位的方式登陆
        bottoms = self.driver.find_elements_by_class_name("android.widget.ImageView")
        print(len(bottoms))#可以打印看下找到了几个name属性为android.widget.ImageView的元素
        count = bottoms[25]
        count.click()
        sleep(1)
        self.driver.find_elements_by_class_name("android.widget.TextView")[0].click()#通过索引来操作元素
        #第二次登陆,没有上面的这一步self.driver.find_element_by_id("cn.com.open.mooc:id/right_text").click()
        self.driver.find_elements_by_class_name("android.widget.EditText")[0].clear()
        self.driver.find_elements_by_class_name("android.widget.EditText")[0].send_keys("15555555555")
        self.driver.find_elements_by_class_name("android.widget.EditText")[1].clear()
        self.driver.find_elements_by_class_name("android.widget.EditText")[1].send_keys("XXXX")
        self.driver.find_elements_by_class_name("android.widget.TextView")[2].click()
        sleep(2)

    def goLoginByUiAutomator(self):
        bottoms = self.driver.find_elements_by_android_uiautomator('new UiSelector().resourceId("cn.com.open.mooc:id/lav")')
        count = bottoms[3]
        count.click()
        sleep(1)
        self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.TextView")')[0].click()
        self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("cn.com.open.mooc:id/right_text")').click()#若是已有账号,那么直接点击登陆跳转到登陆界面;非首次登陆,不需要这一步,点击登陆会直接跳转到登陆界面
        self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.EditText")')[0].clear()
        self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.EditText")')[0].send_keys("15555555555")
        self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.EditText")')[1].clear()
        self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.EditText")')[1].send_keys("XXXX")
        self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.TextView")')[2].click()
        sleep(2)

    def goLoginByXpath(self):
        # self.driver.find_elements_by_xpath('//*[contains(@text,"账号")]')[1].click()#写法一,找到页面上text属性包含“账号“的元素
        # self.driver.find_element_by_xpath('//android.widget.TextView[@text="账号"]').click()#写法二,找到页面上classname为android.widget.TextView并且text属性等于”账号”的元素
        self.driver.find_element_by_xpath('//android.widget.TextView[@text="账号"]/../../preceding-sibling::*[@index="2"]').click()#定位页面上classname为android.widget.TextView并且text等于”账号”的元素;/../../代表向上两个层级,preceding-sibling代表兄弟节点,意思是找到兄弟节点下面index=2的元素

    def getWebView(self):#原生和H5页面之间的切换
        webview = self.driver.contexts
        print(webview)#打印看下当前的上下文窗口有几个,获取要切换的窗口
        for view in webview:
            if view == "XXX":#XXX是要切换过去的窗口
                self.driver.switch_to.content(view)
                break
    def getToast(self):
        sleep(1)
        bottoms = self.driver.find_elements_by_id("cn.com.open.mooc:id/lav")  # 底部的四个按钮(首页,手记,我的学习,账号的id属性是一样的,先找出所有的,然后再通过索引查找需要的元素)
        count = bottoms[3]  # 账号元素的索引为3
        count.click()  # 做一个点击操作
        sleep(1)
        self.driver.find_elements_by_class_name("android.widget.TextView")[0].click()  # 通过索引来操作元素
        self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("cn.com.open.mooc:id/right_text")').click()
        self.driver.find_elements_by_class_name("android.widget.EditText")[0].clear()
        self.driver.find_elements_by_class_name("android.widget.EditText")[0].send_keys("15555555555")
        self.driver.find_elements_by_class_name("android.widget.TextView")[2].click()
        self.driver.find_elements_by_class_name("android.widget.EditText")[1].send_keys("123456")
        self.driver.find_elements_by_android_uiautomator('new UiSelector().className("android.widget.TextView")')[2].click()
        toast_element = ("xpath", "//*[contains(@text,'登录密码错误')]")#//代表根目录,*是通配符。表示根目录下所有text属性包含“登录密码错误”的元素
        try:
            toast = WebDriverWait(self.driver, 10, 0.1).until(EC.presence_of_element_located(toast_element))
            print(toast.text)
        except:
            print("toast未找到")


if __name__ == "__main__":
    handleelement = handleLoginElement()
    handleelement.getToast()

其中,获取toast元素,需要
1、Appium1.6.3以上(包括1.6.3)
2、Android版本在5.0以上
引入了两个包:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

WebDriverWait用于智能等待;expected_conditions用于直到某个条件满足

toast = WebDriverWait(self.driver, 10, 0.1).until(EC.presence_of_element_located(toast_element))

这段代码的含义是:智能等待10s,每隔0.1秒检测一次,直到某个定位元素出现为止。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352