Appium自动化测试

  • Post author:
  • Post category:其他



目录


初始化参数介绍:


例一:自动化播放酷狗音乐


例二:自动化抢微信红包


例三: 刷抖音


例四:自动化微信存活好友检查机制


例五:微信好友检测及删除,升级版


其它:


自动化的显示等待和隐式等待:


隐式等待:


appium中就自带了一个隐式等待的方法了:


显示等待:


要使用显示等待就需要传入一些包:


获取设别的连接参数:


显示可操作的手机设备信息


显示当前设备端开启的应用名


获取apk包的 appPackage/appActivity 值:


安装及配置请移步该链接进行观看:

最新最全最细节的Appium安装配置教程

基础讲解请移步:

appiun的基础知识

多规则筛选级选择器请见:

UiSelector官网介绍

初始化参数介绍:

from time import sleep
from appium.webdriver import Remote   # 该包就是实例化后的webdriver
from appium.webdriver.common.appiumby import AppiumBy  # 导入元素定位的By包

desired = {
    "platformName": "Android",  # 要进行自动化的平台(系统)名称  Android/iOS/FirefoxOS
    "platformVersion": "9",  # 使用的平台的版本号
    "deviceName": "xxx",
    # 使用的手机类型,在 iOS 上,这个关键字的值必须是使用 ' instruments -s devices ' 得到的可使用的设备名称之一。在 Android 上,这个关键字目前不起作用。
    
    "appPackage": "com.kugou.android",  # 要对哪个应用包(app)进行自动化操作
    "appActivity": ".app.splash.SplashActivity",  # 启动程序时要展示的首页面
    "unicodeKeyboard": True,  # True为使用这个输入法,在做自动化测试时需要输入中文的话需要将该参数改为True
    "resetKeyboard": True,  # 当自动化测试结束时将输入法改为测试前使用的输入法
    "noReset": True,  # True为保留原本的 session 信息,可以避免重新登陆。(当值为False时,因为没有携带session,所以启动的程序会相当于刚下载的一样。)
    "newCommandTimeout": 6000,  # 接收命令的前后等待的最长时间(超过该时间的话就结束会话)
    "automationName": "UiAutomator2",  # 要使用的自动化测试引擎

    "app":".apk/.ipa文件所在本地的位置",
    # 将app安装到连接的系统平台中,该参数会与browserName参数起冲突。当指定了appPackage||appActivity时就不需要指定该参数,不然每次启动时都会重新下载一次应用

    "browserName": "Safari/Chrome/Chromium/Browser",
    # 对该手机上的哪个浏览器进行自动化测试,如果是对应用进行自动化测试,这个关键字的值应为空。
    "chromedriverExecutable": r"c:\chromedriver.exe",
    # 对手机端的浏览器进行自动化测试时电脑这边存放的谷歌驱动的位置

    "autoLaunch": True,  # Appium是否需要自动安装和启动应用
    "language": "",  # 设定模拟器的语言
    "locale": "fr_CA",  # 设定模拟器的区域设置
    "udid": "",  # 连接的物理设备的唯一设备标识
    "orientation": "",  # 在一个设定的方向模式中开始测试 LANDSCAPE(横向) / PORTRAIT(纵向)
    "autoWebview": True,  # 直接转换到 WebView 上下文
    "fullReset": True,  # (iOS时) 删除整个模拟器目录。(Android时) 通过卸载--而不是清空数据——来重置应用状态。在 Android 上,这也会在会话结束后自动清除被测应用

# ### Android特有 !!!
    "appWaitActivity": "",  # 你想要等待启动的页面
    "deviceReadyTimeout": "",  # 设置等待一个模拟器或真机准备就绪的超时时间
    "androidCoverage": "",
    # 用于执行测试的 instrumentation 类。作为命令 `adb shell am instrument -e coverage true -w` 的 `-w` 参数。| `com.my.Pkg/com.my.Pkg.instrumentation.MyInstrumentation`
    
    "enablePerformanceLogging": True,  # 仅适用于 Chrome 和 webview 开启 Chromedriver 的性能日志
    "androidDeviceReadyTimeout": "30",  # 等待设备在启动应用后准备就绪的超时时间。以秒为单位。|如 '30'|
    "androidDeviceSocket": "",
    # 开发工具的 socket 名称。只有在被测应用是一个使用 Chromium 内核的浏览器时需要。 socket 会被浏览器打开,然后 Chromedriver 把它作为开发者工具来进行连接。
    
    "avd": "",  # 需要启动的 AVD  (安卓虚拟设备) 名称
    "avdLaunchTimeout": "",  # 以毫秒为单位,等待 AVD 启动并连接到 ADB 的超时时间
    "avdReadyTimeout": "",  # 以毫秒为单位,等待 AVD 完成启动动画的超时时间
    "avdArgs": "",  # 启动 AVD 时需要加入的额外的参数。如 '-netfast'
    "useKeystore": True,  # 是否使用一个自定义的 keystore 来对 apk 进行重签名
    "keystorePath": r"c:\path\to.keystore",  # 自定义 keystore 的路径
    "keystorePassword": "",  # 自定义 keystore 的密码
    "keyAlias": "",  # key 的别名
    "keyPassword": "",  # key 的密码

    
    "autoWebviewTimeout": "",  # 以毫秒为单位,等待 Webview 上下文激活的时间。
    "intentAction": "",  # 用于启动 activity 的 intent action。(默认值:android.intent.action.MAIN) android.intent.action.VIEW
    "intentCategory": "",
    # 用于启动 activity 的 intent category。 (默认值:android.intent.category.LAUNCHER) android.intent.category.APP_CONTACTS
    
    "intentFlags": "",  # 用于启动 activity 的标识 ( flags )  (默认值 `0x10200000`)
    "optionalIntentArguments": "",
    # 用于启动 activity 的额外 intent 参数。请查看 [Intent 参数](http://developer.android.com/tools/help/adb.html#IntentSpec)
    
    "stopAppOnReset": True,
    # 在使用 adb 启动应用前停止被测应用的进程 ( process ) 。如果被测应用是被另一个应用创建的,当这个参数被设定为 false 时,允许另一个应用的进程在使用 adb 启动被测应用时继续存活。
    
    "noSign": False,  # 跳过检查和对应用进行 debug 签名的步骤。只能在使用 UiAutomator 时使用,使用 selendroid 是不行.
    
    "ignoreUnimportantViews": False,
    # 调用 uiautomator 的函数 'setCompressedLayoutHierarchy()。由于 Accessibility 命令在忽略部分元素的情况下执行速度会加快,这个关键字能加快测试执行的速度。被忽略的元素将不能够被找到,因此这个关键字同时也被实现成可以随时改变的 设置 。


# ### iOS特有 !!!
    "calendarFormat": "",  # 为iOS的模拟器设置日历格式| 如 'gregorian' (公历)
    "bundleId": "",
    # 被测应用的 bundle ID 。用于在真实设备中启动测试,也用于使用其他需要 bundle ID 的关键字启动测试。在使用 bundle ID 在真实设备上执行测试时,你可以不提供 `app` 关键字,但你必须提供 `udid` 。|如 `io.appium.TestApp`|
    
    "launchTimeout": "",  # 以毫秒为单位,在 Appium 运行失败之前设置一个等待 instruments 的时间
    "locationServicesEnabled": True,  # 强制打开或关闭定位服务
    "locationServicesAuthorized": True,
    # 通过修改 plist 文件设定是否允许应用使用定位服务,从而避免定位服务的警告出现。请注意在使用这个关键字时,你同时需要使用 `bundleId` 关键字来发送你的应用的 bundle ID。
    
    "autoAcceptAlerts": False,  # 当 iOS 的个人信息访问警告 (如 位置、联系人、图片) 出现时,自动选择接受
    "autoDismissAlerts": False,  # 当 iOS 的个人信息访问警告 (如 位置、联系人、图片) 出现时,自动选择不接受
    "nativeInstrumentsLib": True,  # 是否使用原生 intruments 库 (即关闭 instruments-without-delay )
    "nativeWebTap": True,  # 在Safari中允许"真实的",非基于 javascript 的 web 点击
    "safariInitialUrl": "",  # (>= 8.1) 初始化 safari 时使用的地址,默认是一个本地的欢迎页面
    "safariAllowPopups": True,  # 允许 javascript 在 Safari 中创建新窗口。
    "safariIgnoreFraudWarning": True,  # 阻止 Safari 显示此网站可能存在风险的警告
    "safariOpenLinksInBackground": True,  # Safari 是否允许链接在新窗口打开。
    "keepKeyChains": True,  # 当 Appium 会话开始/结束时是否保留存放密码存放记录 (keychains)  (库(Library)/钥匙串(Keychains))
    "localizableStringsDir": "",  # 从哪里查找本地化字符串。默认值 `en.lproj`
    "processArguments": "",  # 通过 instruments 传递到 AUT 的参数
    "interKeyDelay": "",  # 以毫秒为单位,按下每一个按键之间的延迟时间。
    "showIOSLog": False,  # 是否在 Appium 的日志中显示设备的日志。
    "sendKeyStrategy": "",  # 输入文字到文字框的策略。模拟器默认值:`oneByOne` (一个接着一个) 。真实设备默认值:`grouped` (分组输入)
    "screenshotWaitTimeout": "",  # 以秒为单位,生成屏幕截图的最长等待时间。默认值: 10。
    "waitForAppScript": "",  # 用于判断 "应用是否被启动” 的 iOS 自动化脚本代码。默认情况下系统等待直到页面内容非空。结果必须是布尔类型。 |例如 `true;`, `target.elements().length > 0;`, `$.delay(5000); true;` |
}

"""
访问本地的4723进程内wd/hub下的应用,将指定参数传进去,
该应用通过传进来的参数去查找符合参数的操作系统,对其发起连接,
连接成功后返回连接对象,可对该对象进行操作,
该对象会将操作命令发给其连接的系统中进行操作处理。

"""

driver = Remote('http://localhost:4723/wd/hub',
                desired_capabilities=desired)

例一:自动化播放酷狗音乐

from appium.webdriver import Remote  # 该包就是实例化后的webdriver
from appium.webdriver.common.appiumby import AppiumBy  # 导入元素定位的By包
from appium.webdriver.extensions.android.nativekey import AndroidKey  # 导入安卓的键盘值包

desired = {
    "platformName": "Android",  # 要进行自动化的平台(系统)名称  Android/iOS/FirefoxOS
    "platformVersion": "9",  # 使用的平台的版本号
    "deviceName": "xxx",
    # 使用的手机类型,在 iOS 上,这个关键字的值必须是使用 ' instruments -s devices ' 得到的可使用的设备名称之一。在 Android 上,这个关键字目前不起作用。

    "appPackage": "com.kugou.android",  # 要对哪个应用包(app)进行自动化操作
    "appActivity": ".app.splash.SplashActivity",  # 启动程序时要展示的首页面
    "unicodeKeyboard": True,  # True为使用这个输入法,在做自动化测试时需要输入中文的话需要将该参数改为True
    "resetKeyboard": True,  # 当自动化测试结束时将输入法改为测试前使用的输入法
    "noReset": True,  # True为保留原本的 session 信息,可以避免重新登陆。(当值为False时,因为没有携带session,所以启动的程序会相当于刚下载的一样。)
    "newCommandTimeout": 6000,  # 接收命令的前后等待的最长时间(超过该时间的话就结束会话)
    "automationName": "UiAutomator2",  # 要使用的自动化测试引擎
}

"""
访问本地的4723进程内wd/hub下的应用,将指定参数传进去,
该应用通过传进来的参数去查找符合参数的操作系统,对其发起连接,
连接成功后返回连接对象,可对该对象进行操作,
该对象会将操作命令发给其连接的系统中进行操作处理。

"""

driver = Remote('http://localhost:4723/wd/hub',
                desired)
print("打开应用...")
driver.implicitly_wait(10)  # 在十秒内没有执行成功指令的话就会报错

print("点击我的,进入我的页面...")
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 传入java代码去按指定规则选择元素
                    'new UiSelector().text("我的").resourceId("com.kugou.android:id/v62").index(1)').click()

print("点击我喜欢,进入收藏页面")
driver.find_element(AppiumBy.ID,  # 按指定id选择元素
                    'com.kugou.android:id/bl_').click()

print("点击搜索按钮")
driver.find_element(AppiumBy.XPATH,  # 按xpath选择元素
                    '//android.widget.ImageView[@content-desc="搜索"]').click()

print("输入歌曲名并回车")
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
                    'new UiSelector().text("请输入歌曲名或歌手名")').send_keys('城南花已开')
driver.press_keycode(AndroidKey.ENTER)  # 对设备发起键盘指令

print("点击歌曲进行播放...")
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
                    'new UiSelector().text("城南花已开").resourceId("com.kugou.android:id/b2w")').click()

print("已自动播放音乐...")

"""
1.对指定设备发起连接
2.将处理指令的最长时间设为10秒
3.分别点击-->我的-->我喜欢-->搜索按钮-->搜索框-->输入歌曲名-->按回车-->点击歌曲进行播放

由于设了隐式等待时间,所以每次执行指令会等待最长时间为10,
在十秒内的任意时间中成功执行该条指令的话才会继续执行下一条指令。
"""

例二:自动化抢微信红包

from time import sleep  # 必要时使程序强制进入睡眠,不然因为程序太快使得自动化反应不过来

from selenium.webdriver.support.ui import WebDriverWait  # 导入显示等待
from selenium.webdriver.support import expected_conditions as EC  # 一个和显示等待很好搭配的条件判断包
from selenium.webdriver.common.by import By  # 导入条件判断包需要的元素定位包

from appium.webdriver.common.touch_action import TouchAction  # 导入自动化的触摸空间包,可对程序进行触摸操作
from appium.webdriver.common.appiumby import AppiumBy  # appium的元素定位包
from appium.webdriver import Remote  # 实例化后的自动化连接包

desired = {
    "platformName": "Android",  # 要进行自动化的平台(系统)名称  Android/iOS/FirefoxOS
    "platformVersion": "9",  # 使用的平台的版本号
    "deviceName": "xxx",
    # 使用的手机类型,在 iOS 上,这个关键字的值必须是使用 ' instruments -s devices ' 得到的可使用的设备名称之一。在 Android 上,这个关键字目前不起作用。

    "appPackage": "com.tencent.mm",  # 要对哪个应用包(app)进行自动化操作
    "appActivity": ".ui.LauncherUI",  # 启动程序时要展示的首页面
    "unicodeKeyboard": True,  # True为使用这个输入法,在做自动化测试时需要输入中文的话需要将该参数改为True
    "resetKeyboard": True,  # 当自动化测试结束时将输入法改为测试前使用的输入法
    "noReset": True,  # True为保留原本的 session 信息,可以避免重新登陆。(当值为False时,因为没有携带session,所以启动的程序会相当于刚下载的一样。)
    "newCommandTimeout": 6000,  # 接收命令的前后等待的最长时间(超过该时间的话就结束会话)
    "automationName": "UiAutomator2",  # 要使用的自动化测试引擎
}
driver = Remote('http://localhost:4723/wd/hub', desired)  # 连接自动化程序

driver.implicitly_wait(10)  # 将该次自动化的隐式等待设为10秒,每次指令在十秒内没反应才会报错

wait = WebDriverWait(driver, 120, 0.5)
# 将指定驱动加入到显示等待中,显示等待的最长时间为120秒,当调用其内的until()/until_not()方法时,每0.5秒会执行一次那个方法

driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
                    'new UiSelector().resourceId("com.tencent.mm:id/fzg").text("a,、你瞅下,这是啥。")'
                    ).click()  # 进去群聊页面


while True:
    # 有红包则点击
    wait.until(  # 显示等待120秒,120秒内每0.5秒去获取一次 红包 元素,当获取到时进行点击操作
            EC.element_to_be_clickable(  # 获取一个可被点击的元素
                    (By.ID, "com.tencent.mm:id/u1")  # 这个元素的定位值,为元组形式
            )
    ).click()  # 程序阻塞120秒去获取指定元素

    print("点击了红包")  # 成功获得红包元素,进行点击操作,

    try:
        driver.find_element(AppiumBy.XPATH, '//android.widget.Button[@content-desc="开"]').click()
        print('成功领取到红包')  # 当在拆红包页面中有 “开” 时,代表其没被领完,那就领取红包
        sleep(2)  # 没报错的话就是成功领取了,由于响应有点慢,所以要强制休息两秒让微信反应一下

        driver.find_element(AppiumBy.XPATH, '//android.widget.ImageView[@content-desc="返回"]').click()
        print('返回到群聊界面...1')  # 微信反应过来后去点击返回按钮返回到群里界面

    except:
        print('红包已被领完,未领取到')  # 当没有获取到 "开" 元素的话就会报错,就代表这个红包被领完了
        try:
            # 隐式等待十秒内获取到返回按钮并进行点击,返回群聊界面
            driver.find_element(AppiumBy.ID, 'com.tencent.mm:id/ei').click()
            print('返回到群聊界面...2')  # 先去找返回按钮,没有返回按钮的话就去找打叉按钮
        except:  # 返回按钮是自己领过才会出现的,打叉是别人领完了才会出现的
            # 隐式等待十秒内获取到 打叉(X) 按钮并进行点击,返回群聊界面
            driver.find_element(AppiumBy.ID, 'com.tencent.mm:id/f4e').click()
            print('返回到群聊界面...3')

    # 删除领取过的红包记录

    delete = driver.find_element(AppiumBy.ID, 'com.tencent.mm:id/u1')
    TouchAction(driver).long_press(delete).perform()
    # 获取到要删除的红包元素,将驱动添加到触摸控件中,对要删除的红包元素进行长按操作,并执行命令

    # 长按后显示删除按钮,点击删除按钮
    driver.find_element(AppiumBy.ID, 'com.tencent.mm:id/gef').click()

    # 点击弹出框的删除选项
    driver.find_element(AppiumBy.ID, 'com.tencent.mm:id/ffp').click()

例三: 刷抖音

import keyboard  # 键盘监听库
from appium.webdriver import Remote  # 实例化后的自动化连接包

desired = {
    "platformName": "Android",  # 要进行自动化的平台(系统)名称  Android/iOS/FirefoxOS
    "platformVersion": "9",  # 使用的平台的版本号
    "deviceName": "xxx",
    # 使用的手机类型,在 iOS 上,这个关键字的值必须是使用 ' instruments -s devices ' 得到的可使用的设备名称之一。在 Android 上,这个关键字目前不起作用。

    "appPackage": "com.ss.android.ugc.aweme",  # 要对哪个应用包(app)进行自动化操作
    "appActivity": ".main.MainActivity",  # 启动程序时要展示的首页面
    "unicodeKeyboard": True,  # True为使用这个输入法,在做自动化测试时需要输入中文的话需要将该参数改为True
    "resetKeyboard": True,  # 当自动化测试结束时将输入法改为测试前使用的输入法
    "noReset": True,  # True为保留原本的 session 信息,可以避免重新登陆。(当值为False时,因为没有携带session,所以启动的程序会相当于刚下载的一样。)
    "newCommandTimeout": 6000,  # 接收命令的前后等待的最长时间(超过该时间的话就结束会话)
    "automationName": "UiAutomator2",  # 要使用的自动化测试引擎
}
driver = Remote('http://localhost:4723/wd/hub', desired)  # 连接自动化程序

driver.implicitly_wait(10)  # 将该次自动化的隐式等待设为10秒,每次指令在十秒内没反应才会报错


def down_slide():
    driver.flick(start_x=535, start_y=1300, end_x=535, end_y=800)  # 从开始点快速按住跳到结束点
    
    # driver.swipe(start_x=447, start_y=1480, end_x=528, end_y=262)  # 从开始点滑到结束点
    print('已切换到下一条抖音...')

def up_slide():
    driver.flick(start_x=535, start_y=800, end_x=535, end_y=1300)

    # driver.swipe(start_x=447, start_y=1480, end_x=528, end_y=262)
    print('已切换到上一条抖音...')

keyboard.add_hotkey('down', down_slide)
keyboard.add_hotkey('up', up_slide)

"""
打开抖音,只有监听到按到 z 键才会结束测试。当点击向上键时触发 up_slide()函数
点击向下时触发 down_slide()函数
"""

keyboard.wait('z')

例四:自动化微信存活好友检查机制


from time import sleep  # 必要时使程序强制进入睡眠,不然因为程序太快使得自动化反应不过来
from appium.webdriver.common.appiumby import AppiumBy  # appium的元素定位包
from appium.webdriver import Remote  # 实例化后的自动化连接包

desired = {
    "platformName": "Android",  # 要进行自动化的平台(系统)名称  Android/iOS/FirefoxOS
    "platformVersion": "9",  # 使用的平台的版本号
    "deviceName": "xxx",
    # 使用的手机类型,在 iOS 上,这个关键字的值必须是使用 ' instruments -s devices ' 得到的可使用的设备名称之一。在 Android 上,这个关键字目前不起作用。

    "appPackage": "com.tencent.mm",  # 要对哪个应用包(app)进行自动化操作
    "appActivity": ".ui.LauncherUI",  # 启动程序时要展示的首页面
    "unicodeKeyboard": True,  # True为使用这个输入法,在做自动化测试时需要输入中文的话需要将该参数改为True
    "resetKeyboard": True,  # 当自动化测试结束时将输入法改为测试前使用的输入法
    "noReset": True,  # True为保留原本的 session 信息,可以避免重新登陆。(当值为False时,因为没有携带session,所以启动的程序会相当于刚下载的一样。)
    "newCommandTimeout": 6000,  # 接收命令的前后等待的最长时间(超过该时间的话就结束会话)
    "automationName": "UiAutomator2",  # 要使用的自动化测试引擎
}
driver = Remote('http://localhost:4723/wd/hub', desired)  # 连接自动化程序
driver.implicitly_wait(20)

driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
                    'new UiSelector().resourceId("com.tencent.mm:id/dub").text("通讯录")').click()

xpath = '//android.widget.FrameLayout[@content-desc="当前所在页面,与的聊天"]/' \
        'android.widget.FrameLayout/android.widget.LinearLayout/android.widget.' \
        'FrameLayout/android.widget.FrameLayout[2]/android.view.ViewGroup/android.' \
        'widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.FrameLayout/' \
        'com.tencent.mm.ui.mogic.WxViewPager/android.widget.FrameLayout/android.widget.' \
        'LinearLayout/android.widget.FrameLayout/android.widget.ListView/android.widget.' \
        'LinearLayout'


result_name = []  # 好友名存放列表

count = 1  # 当其值为0时代表已经保存好了所有好友了,可以进行检查操作了
while count:
    eles = driver.find_elements(AppiumBy.XPATH, xpath)  # 拿到好友元素
    for ele in eles:  # 在好友们的元素列表中循环
        try:
            name = ele.find_element(AppiumBy.ID,'com.tencent.mm:id/ft6').text
            if name not in result_name:  # 拿到好友名,判断当这个好友没有存储到列表中的话就进行存储
                count = 1  # 每次将count的值回复到1,当count的值自加到50时代表划到低了
                result_name.append(name)
                print('存储了:',name)
            else:
                count += 1
                if count == 50:
                    count = 0
                    break  # 连续循环了几十次,划到低了,没有好友了,可以退出循环了
        except:
            pass  # 最开始有两个元素是公众号,拿不到指定元素会报错,可以过掉

    driver.swipe(start_x=500,start_y=1700,end_x=500,end_y=1000)  # 好友遍历一次结束后向上滑动屏幕刷新新的好友值


print()
print("*"*20)
print('好友获取结束...\n一共:',len(result_name),'个好友')
print('执行好友检测操作...')

driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
                    'new UiSelector().resourceId("com.tencent.mm:id/dub").text("微信")').click()


def delete():  # 删除好友操作
    driver.find_element(AppiumBy.ID,  # 点击返回到聊天界面
                        'com.tencent.mm:id/ei').click()

    driver.find_element(AppiumBy.XPATH,  # 点击进入个人主页
                        '//android.widget.ImageView[@content-desc="聊天信息"]').click()

    title_img = '//android.widget.FrameLayout[@content-desc="当前所在页面,聊天信息"]/android.widget.' \
        'FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.' \
        'FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.widget.Fram' \
        'eLayout/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.List' \
        'View/android.widget.LinearLayout[1]/android.widget.RelativeLayout[1]/android.widget.ImageView'
    driver.find_element(AppiumBy.XPATH,  # 点击头像跳转到个人页面中
                        title_img).click()

    sleep(1)
    driver.find_element(AppiumBy.ID,  # 点击删除所在页面
                        'com.tencent.mm:id/d8').click()

    driver.find_element(AppiumBy.ID,  # 点击删除按钮
                        'com.tencent.mm:id/ijq').click()

    driver.find_element(AppiumBy.ID,  # 确认删除
                        'com.tencent.mm:id/ffp').click()
    # "删除了好友后会自动的跳回首页"

count = 0
ded = 0
def return_page():  # 检查出没被删除,返回页面

    driver.find_element(AppiumBy.ID,  # 点击返回到聊天界面
                        'com.tencent.mm:id/ei').click()

    driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 拿到这个页面中元素的描述值中有包含 返回 两字的元素标签
                        'new UiSelector().descriptionContains("返回")').click()
    driver.find_element(AppiumBy.ID,  # 点击取消按钮回到微信首页
                        'com.tencent.mm:id/aib').click()


for name in result_name:
    try:  # 因为有些好友是自己,或者微信团队那种,无法获取对应元素的,也无法删除,所以当遇到这种情况时直接跳过
        driver.find_element(AppiumBy.XPATH, # 点击搜索框
                            '//android.widget.RelativeLayout[@content-desc="搜索"]/android.widget.ImageView').click()

        driver.find_element(AppiumBy.ID,  # 定位到搜索输入框,输入名
                            'com.tencent.mm:id/bxz').send_keys(name)

        driver.find_element(AppiumBy.ID,  # 点击搜索到的结果头像,进入到聊天页面中
                            'com.tencent.mm:id/x1').click()

        driver.find_element(AppiumBy.XPATH,  # 点击右下角的加号
                        '//android.widget.ImageButton[@content-desc="更多功能按钮,已折叠"]').click()

        driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 点击转账
                            'new UiSelector().resourceId("com.tencent.mm:id/rt").text("转账")').click()

        driver.find_element(AppiumBy.ID,  # 点击 1
                            'com.tencent.mm:id/e64').click()

        driver.find_element(AppiumBy.ID,  # 点击转账
                            'com.tencent.mm:id/e6c').click()
    except:
        continue


    try:
        driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 拿到这个页面中元素的描述值中有包含 关闭 两字的元素标签
                            'new UiSelector().descriptionContains("关闭")').click()
        return_page()
        count += 1
        print('活人...+', count)


    except:
        driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 有这个元素时代表被人删了
                            'new UiSelector().resourceId("com.tencent.mm:id/ffp").text("知道了")').click()
        print(f'{name} -- 是死人,正在对其进行清除操作...')
        delete()
        ded += 1
        print('清除成功...+', ded)

例五:微信好友检测及删除,升级版

from time import sleep  # 必要时使程序强制进入睡眠,不然因为程序太快使得自动化反应不过来
from appium.webdriver.common.appiumby import AppiumBy  # appium的元素定位包
from appium.webdriver import Remote  # 实例化后的自动化连接包
import os  # 用于创建存放好友的文件
import time  # 查看耗时


class FriendsDetection:

    # 先初始化连接设备,并进行一些条件的判断及条件的满足
    def __init__(self):
        desired = {
            "platformName": "Android",  # 要进行自动化的平台(系统)名称  Android/iOS/FirefoxOS
            "platformVersion": "9",  # 使用的平台的版本号
            "deviceName": "xxx",
            # 使用的手机类型,在 iOS 上,这个关键字的值必须是使用 ' instruments -s devices ' 得到的可使用的设备名称之一。在 Android 上,这个关键字目前不起作用。

            "appPackage": "com.tencent.mm",  # 要对哪个应用包(app)进行自动化操作
            "appActivity": ".ui.LauncherUI",  # 启动程序时要展示的首页面
            "unicodeKeyboard": True,  # True为使用这个输入法,在做自动化测试时需要输入中文的话需要将该参数改为True
            "resetKeyboard": True,  # 当自动化测试结束时将输入法改为测试前使用的输入法
            "noReset": True,  # True为保留原本的 session 信息,可以避免重新登陆。(当值为False时,因为没有携带session,所以启动的程序会相当于刚下载的一样。)
            "newCommandTimeout": 6000,  # 接收命令的前后等待的最长时间(超过该时间的话就结束会话)
            "automationName": "UiAutomator2",  # 要使用的自动化测试引擎
        }
        self.driver = Remote('http://localhost:4723/wd/hub', desired)  # 连接自动化程序
        self.driver.implicitly_wait(20)

        if not os.path.exists('live.txt'):
            open('live.txt', 'w', encoding='utf-8')

        if not os.path.exists('death.txt'):
            open('death.txt', 'w', encoding='utf-8')

        if not os.path.exists('exception.txt'):
            open('exception.txt', 'w', encoding='utf-8')  # 当当前目录内没有这些文件的话就创建

    # 获取到所有好友的名称
    def get_friends(self):
        self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 点击通讯录
                                 'new UiSelector().resourceId("com.tencent.mm:id/dub").text("通讯录")').click()
        print('开始获取所有的好友...')
        xpath = '//android.widget.FrameLayout[@content-desc="当前所在页面,与的聊天"]/' \
                'android.widget.FrameLayout/android.widget.LinearLayout/android.widget.' \
                'FrameLayout/android.widget.FrameLayout[2]/android.view.ViewGroup/android.' \
                'widget.FrameLayout[1]/android.widget.FrameLayout/android.widget.FrameLayout/' \
                'com.tencent.mm.ui.mogic.WxViewPager/android.widget.FrameLayout/android.widget.' \
                'LinearLayout/android.widget.FrameLayout/android.widget.ListView/android.widget.' \
                'LinearLayout'  # 好友们所在的位置

        friends = []  # 好友名存放列表

        count = 1  # 当其值为0时代表已经保存好了所有好友了,可以进行检查操作了
        while count:
            friends_ele = self.driver.find_elements(AppiumBy.XPATH, xpath)  # 拿到好友元素
            for ele in friends_ele:  # 在好友们的元素列表中循环
                try:
                    name = ele.find_element(AppiumBy.ID, 'com.tencent.mm:id/ft6').text
                    if name not in friends:  # 拿到好友名,判断当这个好友没有存储到列表中的话就进行存储
                        count = 1  # 每次将count的值回复到1,当count的值自加到50时代表划到底了,因为连续五十次好友都在列表中
                        friends.append(name)
                    else:
                        count += 1
                        if count == 50:
                            count = 0
                            break  # 连续循环了几十次,划到底了,没有好友了,可以退出循环了
                except:
                    pass  # 最开始有两个元素是公众号,拿不到指定元素会报错,可以过掉

            self.driver.swipe(start_x=500, start_y=1700, end_x=500, end_y=1000)  # 好友遍历一次结束后向上滑动屏幕刷新新的好友值

        with open('friends.txt', 'w', encoding='utf-8') as fp:
            for name in friends:  # 将好友们存到文件中
                fp.write(name + '\n')

            print('已获取到所有的好友名单...\n存放于当前目录中的 friends.txt 中')
            fp.close()

    # 获取到把我删了的人
    def get_delete_friends(self, friends):
        self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 点击进入首页
                                 'new UiSelector().resourceId("com.tencent.mm:id/dub").text("微信")').click()
        print('开始检测好友的存活情况...')

        survival = 0
        die = 0

        with open('live.txt', 'r', encoding='utf-8') as live_f, \
                open('death.txt', 'r', encoding='utf-8') as death_f, \
                open('exception.txt', 'r', encoding='utf-8') as exception_f:

            ok = ''.join(live_f.readlines()).split('\n')[:-1] + \
                 ''.join(death_f.readlines()).split('\n')[:-1] + \
                 ''.join(exception_f.readlines()).split('\n')[:-1]

            for name in ok:  #
# 打开三个文件,判断这三个文件中的好友是否在检测的列表中,如果在的话就移除,因为已经检测过一次了没必要再去检测一次
                if name in friends:
                    friends.remove(name)
            live_f.close()
            death_f.close()

        for name in friends:  # 执行好友检测
            try:  # 因为有些好友是自己,或者微信团队那种,无法获取对应元素的,也无法删除,所以当遇到这种情况时直接跳过
                self.driver.find_element(AppiumBy.XPATH,  # 点击搜索框
                                         '//android.widget.RelativeLayout[@content-desc="搜索"]/android.widget.ImageView').click()

                self.driver.find_element(AppiumBy.ID,  # 定位到搜索输入框,输入名
                                         'com.tencent.mm:id/bxz').send_keys(name)

                self.driver.find_element(AppiumBy.ID,  # 点击搜索到的结果头像,进入到聊天页面中
                                         'com.tencent.mm:id/x1').click()

                self.driver.find_element(AppiumBy.XPATH,  # 点击右下角的加号
                                         '//android.widget.ImageButton[@content-desc="更多功能按钮,已折叠"]').click()

                self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 点击转账
                                         'new UiSelector().resourceId("com.tencent.mm:id/rt").text("转账")').click()

                self.driver.find_element(AppiumBy.ID,  # 点击 1
                                         'com.tencent.mm:id/e64').click()

                self.driver.find_element(AppiumBy.ID,  # 点击转账
                                         'com.tencent.mm:id/e6c').click()
            except:
                try:
                    self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 拿到这个页面中元素的描述值中有包含 返回 两字的元素标签
                                             'new UiSelector().descriptionContains("返回")').click()
                    self.driver.find_element(AppiumBy.ID,  # 点击取消按钮回到微信首页
                                             'com.tencent.mm:id/aib').click()
                    continue
                except:
                    continue

            try:
                self.driver.implicitly_wait(5)

                self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 拿到这个页面中元素的描述值中有包含 关闭 两字的元素标签
                                         'new UiSelector().descriptionContains("关闭")').click()
                with open('live.txt', 'a', encoding='utf-8') as f:
                    f.write(name + '\n')  # 将没把我删除的好友存放到文件中
                    f.close()
                self.driver.find_element(AppiumBy.ID,  # 点击返回到聊天界面
                                         'com.tencent.mm:id/ei').click()

                self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 拿到这个页面中元素的描述值中有包含 返回 两字的元素标签
                                         'new UiSelector().descriptionContains("返回")').click()
                self.driver.find_element(AppiumBy.ID,  # 点击取消按钮回到微信首页
                                         'com.tencent.mm:id/aib').click()
                survival += 1
                # print('没删除我的人...+', survival)


            except:
                try:  # 当其内容不是说被删的话就会报错,表示这个好友是异常好友
                    code = 'new UiSelector().resourceId("com.tencent.mm:id/ffh").text("你不是收款方好友,对方添加你为好友后才能发起转账")'
                    self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 有这个元素时代表被人删了
                                             code)

                    self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 点击知道了
                                             'new UiSelector().resourceId("com.tencent.mm:id/ffp").text("知道了")').click()

                    with open('death.txt', 'a', encoding='utf-8') as f:
                        f.write(name + '\n')  # 将把我删除的好友存放到文件中
                        death_f.close()
                    self.driver.find_element(AppiumBy.ID,  # 点击返回到聊天界面
                                             'com.tencent.mm:id/ei').click()

                    self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 拿到这个页面中元素的描述值中有包含 返回 两字的元素标签
                                             'new UiSelector().descriptionContains("返回")').click()

                    self.driver.find_element(AppiumBy.ID,  # 点击取消按钮回到微信首页
                                             'com.tencent.mm:id/aib').click()
                    die += 1
                    print(f'{name} -- 把我删除了...被 - {die} - 人删除')

                except:
                    self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 点击知道了
                                             'new UiSelector().resourceId("com.tencent.mm:id/ffp").text("知道了")').click()

                    with open('exception.txt', 'a', encoding='utf-8') as f:
                        f.write(name + '\n')  # 账号异常的好友存放到文件中
                        f.close()
                        print(f'{name} -- 是异常好友...')
                    self.driver.find_element(AppiumBy.ID,  # 点击返回到聊天界面
                                             'com.tencent.mm:id/ei').click()

                    self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,  # 拿到这个页面中元素的描述值中有包含 返回 两字的元素标签
                                             'new UiSelector().descriptionContains("返回")').click()

                    self.driver.find_element(AppiumBy.ID,  # 点击取消按钮回到微信首页
                                             'com.tencent.mm:id/aib').click()

        print('已筛选出所有删除了我的人...'
              '\n当前目录中的 live.txt 存放的是没删除我的人'
              '\n当前目录中的 death.txt 存放的是把我删掉的人'
              '\n当前目录中的 exception.txt 存放的是账号异常的人')

    # 执行删除好友操作
    def delete_friend(self, die_friends):
        self.driver.implicitly_wait(30)
        for name in die_friends:
            # 因为有些好友是自己,或者微信团队那种,无法获取对应元素的,也无法删除,所以当遇到这种情况时直接跳过
            self.driver.find_element(AppiumBy.XPATH,  # 点击搜索框
                                     '//android.widget.RelativeLayout[@content-desc="搜索"]/android.widget.ImageView').click()
            sleep(1)
            self.driver.find_element(AppiumBy.ID,  # 定位到搜索输入框,输入名
                                     'com.tencent.mm:id/bxz').send_keys(name)
            sleep(1)
            self.driver.find_element(AppiumBy.ID,  # 点击搜索到的结果头像,进入到聊天页面中
                                     'com.tencent.mm:id/x1').click()
            sleep(1)
            self.driver.find_element(AppiumBy.XPATH,  # 点击进入个人主页
                                     '//android.widget.ImageView[@content-desc="聊天信息"]').click()
            sleep(1)
            title_img = '//android.widget.FrameLayout[@content-desc="当前所在页面,聊天信息"]/android.widget.' \
                        'FrameLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.' \
                        'FrameLayout/android.view.ViewGroup/android.widget.FrameLayout[2]/android.widget.Fram' \
                        'eLayout/android.widget.RelativeLayout/android.widget.LinearLayout/android.widget.List' \
                        'View/android.widget.LinearLayout[1]/android.widget.RelativeLayout[1]/android.widget.ImageView'
            self.driver.find_element(AppiumBy.XPATH,  # 点击头像跳转到个人页面中
                                     title_img).click()
            sleep(1)
            self.driver.find_element(AppiumBy.ID,  # 点击删除所在页面
                                     'com.tencent.mm:id/d8').click()
            sleep(1)
            self.driver.find_element(AppiumBy.ID,  # 点击删除按钮
                                     'com.tencent.mm:id/ijq').click()
            sleep(1)
            self.driver.find_element(AppiumBy.ID,  # 确认删除
                                     'com.tencent.mm:id/ffp').click()
            
            # 将删除掉的好友从要删除的好友文件中移除
            with open('death.txt', 'r', encoding='utf-8') as death_f, \
                    open('exception.txt', 'r', encoding='utf-8') as exception_f:
                all_death = death_f.readlines()
                all_exception = exception_f.readlines()

                if name+'\n' in all_death:
                    all_death.remove(name + '\n')
                    with open('death.txt', 'w', encoding='utf-8') as death_ff:
                        death_ff.writelines(all_death)
                        death_ff.close()

                if name+'\n' in all_exception:
                    all_exception.remove(name + '\n')
                    with open('exception.txt', 'w', encoding='utf-8') as exception_ff:
                        exception_ff.writelines(all_exception)
                        exception_ff.close()
                death_f.close()
                exception_f.close()
            sleep(1)
            print(f'已成功清除了 {name} ...')
            # "删除了好友后会自动的跳回首页"


if __name__ == "__main__":
    sta = time.time()
    
    start = FriendsDetection()
    start.get_friends()  # 获取到好友列表
    with open('friends.txt','r',encoding='utf-8') as friends_f:
        friends_list = ''.join(friends_f.readlines()).split('\n')[:-1]
        friends_f.close()
    start.get_delete_friends(friends_list)  # 将好友列表传到加检测好友存活的方法中
    
    with open('death.txt', 'r', encoding='utf-8') as death_fp, \
            open('exception.txt', 'r', encoding='utf-8') as exception_fp:
        delete_friends = ''.join(death_fp.readlines()).split('\n')[:-1] + \
                         ''.join(exception_fp.readlines()).split('\n')[:-1]
        death_fp.close()
        exception_fp.close()
    start.delete_friend(delete_friends)  # 将要删除的好友传入到删除方法中

    end = time.time()
    print('结束自动化,总耗时:', end - sta)
    
'''
先初始化连接和执行一些必要的东西  --->  获取到好友列表  --->  获取把我删掉的好友列表  
---> 删掉要删除的好友

注意:
    1.文件打开后要立即关闭,with方法只有到程序运行结束后才会关闭文件,这样会使得内存污染,所以文件每次调用完就得立即关闭

'''

其它:


自动化的显示等待和隐式等待:


隐式等待:

  • 顾名思义,隐式等待就是指将该次自动化测试执行命令的超时时间设为指定时间,进行测试的所有命令在指定时间内的(任意时间中)没成功执行指定命令的话就会报错


  • appium中就自带了一个隐式等待的方法了



from appium.webdriver import Remote
driver = Remote('http://localhost:4723/wd/hub',{对设备连接的参数})
driver.implicitly_wait(10)


显示等待:

  • 显示等待是对单个元素设置的等待,


    比如:


    要查找一个按钮元素,如果通过隐式等待去等待那个元素的出现的话,那么会导致整个测试的所有指令的等待时长都会因为给这个元素设置的等待时长而变得很长,所以就需要用到显示等待,显示等待可以给单条指令设置他的等待时长,从而不会影响到全局的等待时间。


    比如:


    执行查找按钮元素,设置显示等待120秒,120秒内没找到就会报错,找到了就继续下一步,下一步的指令因为没有设置显示等待所以执行的时长不是120秒而是隐式等待的10秒。

  • 要使用显示等待就需要传入一些包:

from selenium.webdriver.support.ui import WebDriverWait  # 显示等待包
from selenium.webdriver.support import expected_conditions as ec  # 配合显示等待的条件判断包
from appium.webdriver.common.mobileby import MobileBy as By  # 配合条件判断的元素查找包
wait = WebDriverWait(driver=driver, timeout=120, poll_frequency=0.5, ignored_exceptions=None)  #
'''
    driver: 将指定驱动加入到显示等待中
    timeout: 最长的等待时长,超过这个时长还没执行成功指令的话就报错
    poll_frequency: 几秒去执行一次指令,直到超时才会停止
    ignored_exceptions: 在执行的过程中要忽略什么异常,默认为None,可以不填,默认NoSuchElementExeception
'''

# expected_conditions中有三个比较常用的方法,分别是:
'''
    presence_of_element_located : 判断某个元素是否被加载到 dom 树里,但该元素不一定可见
    visibility_of_element_located : 判断元素是否可见(可见代表元素非隐藏,并且元素宽和高都不等于 0)
    element_to_be_clickable : 判断某个元素中是否可见并且可点击
'''



显示等待必须得搭配其内的两个方法进行操作


until() / until_not()

wait.until(method=EC.element_to_be_clickable((By.ID, "com.tencent.mm:id/u1")),
        message='超时还没找到的话返回的信息')
# 每0.5秒去获取一次这个元素,120秒后(返回值还是False)没找到的话就报错。
# 调用driver提供的方法作为参数,直到返回值不是False。

wait.until_not(method=EC.element_to_be_clickable((By.ID, "com.tencent.mm:id/u1")),
        message='超时还没找到的话返回的信息')
# 每0.5秒去获取一次这个元素,120秒后(返回值还是True)这个元素还没消失的话就报错。
# 调用driver提供的方法作为参数,直到返回值为False


获取设别的连接参数:


显示可操作的手机设备信息

adb devices -l


显示当前设备端开启的应用名

  • 手机端将要获取其 appPackage/appActivity 值的应用开起来显示在最上面,然后在终端运行以下命令
adb shell dumpsys activity recents | find "intent={"

''' 该命令的作用是:获取到当前设备中启动的应用的信息,cmp里对应的就是appPackage/appActivity的值 '''


获取apk包的 appPackage/appActivity 值:

  • 当电脑端有apk包时,怎么拿到其appPackage/appActivity值:
  • 到终端中运行以下命令,拿到appPackage的值:
安卓sdk安装所在目录下的\build-tools\29.0.3\aapt.exe dump badging apk包.apk | find "package: name="

''' 该命令的作用是:运行aapt.exe程序中的dump内的badging方法,获取apk包的信息。|过滤其他信息,留下apk包的name的信息 '''

  • 到终端中运行以下命令,拿到appActivity的值:
安卓sdk安装所在目录下的\build-tools\29.0.3\aapt.exe dump badging apk包.apk | find "launchable-activity"

''' 该命令的作用是:运行aapt.exe程序中的dump内的badging方法,获取apk包的信息。|过滤其他信息,留下apk包的activity的信息 '''



版权声明:本文为adjnd原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。