Appium手机端自动化详解 ——>改

发布时间 2023-07-25 14:07:54作者: 至高无上10086

1:使用appium自动化手机app时机型的选择

建议使用机型:pix(谷歌原生的手机),三星,索尼,小米,魅族  这些手机系统定制化没有那么深,
  可以appium进行自动化,遇到的问题比较少,遇到问题尝试升级系统解决,那么不需要更换手机 不建议使用机型:oppo,vivo,华为(部分华为,有些机型有问题) 这些机型系统深度定制,和安卓原生系统差距比较大,大概率会碰到问题

2:appium 

开源,跨平台,多语言支持的移动应用自动化的工具,
开源(主要依赖selenium提供的webdriver技术,跨平台-能测试ios和安卓)
通俗来说就是 手机自动化 的工具 ,资源整合 平台,利用 手机 自带的 测试框架 来完成自动化
appium类似包工头(利用手机系统自带的测试框架来完成自动化测试),类似老板,安排员工进行测试
selenium分支发展来的,

3:移动端应用来划分:测试app类型:

1:原生app(安卓/ios) 
  原生app,这个应用的 所有 控件 都是由 平台自身 的 代码 所构成的
2:混合(Hybrid)app(H5)
  腾讯课堂,混合应用,大多是 原生应用 的壳里套了一个 网页   美团店铺 商家 内容展示的 服务器上 的数据,数据通过网页形式展现在手机上的,套的网页。和微信)
3:移动web --app(手机网页)  app(就是一个应用) 网页网站也是一个应用

4:原生应用和混合应用的区别(比较直观的方法)

1原生app:有字长按无法选中,  混合应用:长按文字内容可以被选中
2:原生app 只是从 开发架构 和 开发技术上 考虑的

5:被测应用运行的平台,测试手机app,app在哪里运行,被测app运行的平台可以是ios或者安卓

1:appnium不具备手机自动化的能力,靠手机 操作系统 自身测试框架,调用 手机 操作系统 自身 测试框架 需要一个驱动
  如:安卓ui Automotor1
/ui Automotor2(基于1改造后的)(驱动) 2:Automotor2 安卓本身并不自带,想用这个驱动话需要安装,但不需要自己去网上下载安装,通过 执行脚本自动安装 3:桌面端的自动化,很大的内容,开发技术很多,appium自动化也只是其中一种技术。Windows(win10系统) 4:WinAppDriver(能够自动化windows原生的控件) 现在前端工具很多用到js编码编写的,不可以windows自动化 5:appium是js开发的,无法自动化WinAppDriver类型的控件,windows平台开发技术很复杂,元素定位很混乱,没有一个统一的技术 6:ios(苹果系统):XCUITest(驱动) 
  ios自动化需要appium server运行在 mac系统上,代码稍微改一下也能在ios自动化
--会ios自动化环境搭建就行

6:appium架构原理(怎么扮演包工头角色)

test script   appium    Device Automation      Android Device
test script + appium server + Device Automation  是 pc 端运行架构
Android Device  是手机 app 所在的平台
现在学习的自动化针对另外一台设备的自动化,跨硬件,selenium web ui是在本设备上运行 (cs架构) appium整体上是一个cs架构(c:test script s:appium ) test script:
  编写的自动化脚本,客户端client,自动化脚本扮演的角色就是 客户端,写代码的时候不能直接控制浏览器,需要通过中间媒介,
  代码所以只扮演客户端和遥控器的角色,selenium 通过driver才能控制的,appium架构更复杂:
1:test script:
  客户端脚本,点击哪些元素,输入哪些字符控件都是通过代码来完成,代码指令发送手机并且让手机执行,需要经历一系列步骤,
2:appium server:
  服务端
3:测试平台sdk:
  Device Manipulation
4:手机端自动化驱动程序:
  Android Device
pc端架构逻辑:
  1:代码发送给 appium server,(通过http请求指令发给(Appium server))
  2appium server 需要解析一下指令(appium既能测试 ios 也能测试安卓,这里发送给安卓和ios指令不一样,操作的指令 调用的序列不同,所以需要经过解析)
    我们写 代码 不关注你是 ios 还是安卓的,各种元素操作就行,直接写就行,都是一套api,
  3appium server 解析指令之后,调用测试平台的 sdk(sdk:安卓开发需要安卓提供的 sdk,
    sdk 软件开发包,里面包含一些 自动化测试工具。还有一些系统对外提供的api
    手机系统需要对外提供一些 接口 才能去操作它,不然 操作 不了,需要官方提供的标准的 api 库才能去调用,
    标准的 api 库和自动化测试工具统称为 sdk,集合在一个sdk的工具包里面,安卓有安卓的sdk,ios有ios的sdk,通过安卓sdk工具去连接我们的设备,并且向我们的设备发送指令
手机端架构逻辑:
  4:需要手机端的驱动,最重要一步,设备能不能支持自动化,主要看这个
    手机端驱动:
      ios 9.3和以上:XCUITest驱动(ios闭源,自动化必须mac下运行)大部分机型,这是苹果自研的系统 
        自动化  XCUITest 必须要安装到mac下面。无法通过windows自动化 XCUITest
      ios9.3和以下:UIAutomation驱动    很少
      安卓 4.2+:    Google's
        UIAutomator/UIAutomator2驱动     UIAutomator2是appium研发的  (UIAutomator1是安卓自带的,原生的 )
      Windows:  Microsoft's WinAppDriver
自动化手机 appium 本质上就是 操作 驱动进行自动化(需要连接驱动才能自动化)

7:安卓底层自动化架构:了解下安卓驱动怎么工作的

uiAutomator controller(理解为命令转换器),测试命令脚本发送过来的指令需要经过它转化,然后才发送给手机

webdiver script(测试脚本)       <——>     Appium server(类似服务器) <——>             uiAutomator command server(手机上的工具,tcp server)
                             uiAutomator command client(tcp client)   tcp server 接收到命令之后,调用底层的驱动,驱动去做个自动化
                             它将appium解析好的命令通过 scokert指令
                             发送给手机上面的工具

8:appium自动化环境安装,问题,原理环境 

一:安卓手机建议6.0以上的真机
  低于6.0一些命令可能用不了,查看 包 ,路口 信息等一些命令可能使用不了
二:计算机(windows)or Mac
  win10(win7微软已经不维护了)
  内存8g
  硬盘可用内存:4g
  分辨率:1280*800以上
三:Windows机器安装第三方库:pip install Appium-python-Client  appium这个工具支持很多语言,java c等,我们使用python,所以安装 Appium-python-Client
  1.appium 像是一个服务,appium-python-client 是让 python 连接 appium 服务的一个驱动,
    也就是一个 python 语言封装和 appium api 通讯的一个库(python,java,js等)
  2.appium 又继承了 seleniumwebdriver,因此 appium-python-client 安装可能需要更新 selenium。
    要确保安装匹配版本的 seleniumappiumappium 依赖 selenium,所以需要匹配 selenium
安装的 appium
-python-client 依赖的 selenium 需要对应版本   pip install selenium -U    u:upgrade,selenium已安装就升级到最新版。appium和selenium版本匹配
四:安装Appium 服务端, appium Desktop(windows版本) 压缩包的桌面版本  
  测试脚本发送 指令 发送给服务端的,
  Appium 服务端是可以通过命令行 启动(需要另外一套安装方法,暂时不介绍,现在安装 服务端 是 桌面版本的,桌面版本的服务端有个 元素 定位工具)   桌面版本的 Appium 安装简单,下载好直接安装就行,(桌面版本服务端,手动启动,元素定位工具)   安装1.13版本的,装过高版本的再装了 1.15 版本需要管理员权限启动 appium,默认端口号 4723   其他版本使用可能有问题(安装1.13版本)重新换成1.13版本的有个问题:   如果之前装过高版本,再去启动appium server的时候需要右键管理员权限启动appium,   appium server 默认端口号 4723,可以修改的,如果端口号冲突可以修改的
五:安装安卓 sdk
  包下载下来解压到 某个文件夹,然后 配置 环境 就行
  (sdk很多 工具 都是 java编写的,所以 需要安装 java 环境jdk
  (adb命令在 E:\androidsdk\platform
-tools 目录里面)   环境变量搭建:选择系统环境变量     1:系统环境变量新建一个 ANDROID_HOME 变量,变量值为 E:\androidsdk     2:然后在path路径追加 platform-tools 路径: E:\androidsdk\platform-tools
六:安装 jdk(1.8版本)【JAVA环境】
  java运行环境:java命令在 jdk 的 bin 文件目录,adb 命令在 sdk的 platform-tools 里面
  环境变量本质就是配置应用程序所在的目录,系统根据环境变量找这个执行命令,根据环境变量找可执行程序,
  需要配置环境变量:找到盘目录对应的路径
  下载jdk安装包,双击jdkxxx.exe的文件运行,
  然后配置环境变量:
    系统变量名:JAVA_HOME          C:\Program Files (x86)\Java\jdk1.8.0_172 :系统变量值
    PATH                  C:\Program Files (x86)\Java\jdk1.8.0_172\bin;C:\Program Files (x86)\Java\jre1.8.0_172\bin
                         或者:%java home %bin            环境变量相对变量配置,  这种相对环境变量也可以
七:手机端环境配置
  针对另外的一个 系统做 自动化,如果 win7 系统需要安装电脑针对手机 usb 的驱动,否则可能无法识别(手机助手,或者对应品牌的官网下载安装),
  电脑上不要安装 360 哪些自带 adb 的工具,否则会和 安卓sdk 里面的 adb 冲突,很多工具带adb客户端,为了连接手机,我们需要用自己的
  
1:手机端打开usb相关的设置  【 手机打开开发者模式 】【 启动Usb调试 】     usb线连接手机和电脑     进入手机设置-关于手机     不断点击版本号(7)次以上,激活开发者模式 -- 点击定制化系统的版本号,     退出到上级菜单,在开发者模式中,启动usb调试 让你选择手机模式,选择 usb 连接为 MTP媒体传输模式     手机端设置 usb 连接为 MTP 媒体传输模式     确认授权电脑调试   2:启动adb命令 adb devices -l   3:确保电脑端没有安装其他adb程序软件
    如:杀毒软件,安全管家手机助手等,可能造成adb冲突,禁止自动化脚本运行(确保环境整洁没问题)   
4:确保手机端开发者选项中相应的adb权限全部打开
    有的手机打卡 usb权限 还不够,,
    1:除了打开 usb调试
    2:还需要关闭 监控adb安装命令, 有的除了usb调试,还要打开usb安装,还需要打开usb调试安全
----开发端口   5:确保 手机端 appium 相关的app对应权限全部打开        appium 相关的 app 只要第一次 执行脚本 才会正确去安装     设置 ——> 应用管理 ——> 权限设置 ——> 对应的app,查看权限
八:首次运行脚本手机 自动新增 两个app
  appium -settings    应用会自动安装并 启用     服务形式去启动
  UiAutomator2       appium 优化后的 安卓底层 自动化驱动,
  这两个app 确保可以自动安装,并开启全部权限
  我们有些手机不支持 UiAutomator2,那么使用 UiAutomator1,需要加相关的操作,2的性能更好。
  UiAutomator2这个应用没有图标,设置里面
--所有应用里可以搜索
九:代码验证环境,代码能够自动化启动被测应用就代表环境安装成功

9:查看被测 app包名 和 入口信息:重点

手机打开被测 app 然后电脑端cmd输入:adb shell dumpsys activity recents | findstr intent={  
  如果连接多台手机,-s去指定设备名称查询:
    adb -s 设备id(唯一标识) shell dumpsys activity recents | findstr intent={    【获取 被测 app相关信息】
  查看cmd里面打印的第一条结果:cmp=后面的内容全部复制,后面内容就是   :包名/入口信息,一一对应去填写
  如:cmp=com.miui.home/.launcher.Launcher}就是包名和入口信息
  intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10800000 cmp=com.miui.home/.launcher.Launcher}

app没有安装怎么通过app包获取包名和入口信息:   使用sdk提供的工具:在 E:\androidsdk\build
-tools\29.0.3\aapt.exe   cmd切换到 E:\androidsdk\build-tools\29.0.3\aapt.exe 程序所在的目录下面,   因为这个目录没有添加环境变量,不能在任意目录使用命令   命令:aapt dump badging + adk路径   查看 pachage 的(包名+路口信息) adk包可以直接拖到命令行     launchable -activity:name=....(入口信息)     包名:在开头package name =     入口信息:在下面点:launchable -activity:name=....(入口信息) 载入的时候需要启动的activity,就是入口信息

10:简单自动化boss直聘代码

from appium import webdriver        #1:导包

# 准备自动化配置信息,Appiam可以测试安卓和ios,可以 测试手机上的某个应用,
# 那么要告诉 appium 到底要测试哪些,这些信息通过字典配置项传递进去的   如下
desired_caps = {
    #1:被测平台信息三项:platformName,plathformVersion,deviceName
    'platformName': 'Android',      # 移动设备平台--被测平台信息,ios还是安卓,
    'plathformVersion': '10',        # 平台OS版本号,写整数位即可   安卓版本号,
    'deviceName': 'test0106',       # 设备的名称--值可以随便写,但是配置项必须得有

    #2:被测app信息两项:appPackag,appActivity
    'appPackage': 'com.hpbr.bosszhipin',                # 包名
    'appActivity': '.module.launcher.WelcomeActivity',  # 入口信息

    #3:其他项
    'noReset': True,        # 自动化时候自行注册和登录boss直聘,跑完自动化脚本后,如果不写这个配置项,会把登录信息用户信息清空
                            # 相当于重新安装了应用一样,为了避免重置app清空app信息+这个配置项

    'newCommandTimeout': 6000,  #设置session的超时时间,单位秒,默认60s
                                # 元素调试的时候很有用,代码和appium server连接时候需要创建一个session
                                # 待会元素定位时候也是依据这个原理,这中间会有很长一段时间代码不向appiumserver发送指令
                                # 造成超时断开,默认时间60s,把设置时间拉长600s或者6000s

    # 'automationName': 'UiAutomator2',   # 设置底层测试驱动为UiAutomator2,1.15默认使用的底层驱动就是UiAutomator2
                                         # 手机不支持ui2就用ui1或者UiAutomator1
    'automationName': 'UiAutomator1',  
    'unicodeKeyboard': True,
    'resetKeyboard': True
    # 'skipServerInstallation':True       #跳过UI2的安装配置项,如果第一次运行程序,不要添加该配置
}

driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps) #2:创建webdriver对象 - --appium使用Remote返回一个driver对象
    # 参数1:'http://127.0.0.1:4723/wd/hub'
        # appium服务器的ip和端口地址,客户端脚本需要向服务端发送命令,http协议发送
    # 参数2:desired_caps:要告诉appium要自动化的信息,什么手机,什么app,什么配置

driver.implicitly_wait(10)          # 隐式等待
driver.find_elements_by_id('com.hpbr.bosszhipin:id/img_icon')[1].click()  # 点击放大镜搜索按钮先取所有符合条件的元素
search_input = driver.find_element_by_id('com.hpbr.bosszhipin:id/et_search')
search_input.send_keys('软件测试')
# 搜索框输入职位信息,输入关键字,有的界面 输入框点击一下成为可输入的时候
# 可能元素属性发送了变化,所有每次输入需要点击观察一下--没变可以直接操作元素(这种情况注意一下)

driver.find_element_by_id('com.hpbr.bosszhipin:id/tv_filtered_name').click()
# 输入回车或者选择第一个结果,webui可以 \n来模拟回车,appium里面不行, \n在安卓系统不会被解析为回车,submit提交也没这个功能

job_msg = driver.find_elements_by_id('com.hpbr.bosszhipin:id/view_job_card')
# 获取当前页面所有职位信息元素    找大的元素--返回列表

for job in job_msg:             #遍历大列表循环输出:
    name = job.find_element_by_id('com.hpbr.bosszhipin:id/tv_position_name')     # 输出岗位名称
    salray = job.find_element_by_id('com.hpbr.bosszhipin:id/tv_salary_statue')   # 输出薪资
    company = job.find_element_by_id('com.hpbr.bosszhipin:id/tv_company_name')   # 输出公司名称
    print('%s|%s|%s' % (name.text, salray.text, company.text))
driver.quit()       #退出app
# 结束自动化session--结束session之后不能通过当前session来进行定位,需要重新启动session

11:appium自带元素定位工具inspector

ui自动化就是 选择元素 + 操作元素:
web ui自动化定位元素,chrome浏览器自带工具可以定位,手机没有这样 的 工具,需要借用外部工具查看元素,debug元素,
首推:appium 自带的定位工具 inspector   appium自带元素定位工具打开方式:   进入方式与打开方式     
1:创建新的session(执行结束后,driver.quit断开了session,session连接已经断开了就没法attach了--)     2:attach已有的session       新建已有的session,配置信息一个个原封不动都copy复制进去       Automatic Server ——> Desired Capabilities 配置信息一个个复制进去 ——> 点击 save as 保存
        ——
> Saved Capability Sets 点击查看保存的配置 ——> 选中一个保存的信息点击start session就可以进去了
appium自带元素定位工具功能:
  1:最左边是当前同步的画面
  2:三种模式,默认选择元素模式,需要点击一下激活,
  3:最右测有选择元素的信息,这个信息有class,elementid,等信息--
    元素定位的时候就是根据这些属性信息定位的,除了元素自身的属性,
    还可以通过元素的层级关系进行定位-xml结构的
  4:中间的放大镜--验证表达式是不是可以唯一定位

12:appium选择元素的方法和web不同,选元素主要根据以下三种方法  resource-id  content-desc  xpath

1:resource-id:
  app中id不一定唯一定位,手机自动化和web自动化的区别,id其实是控件的属性   id可以省略包名和
/。也能定位到,初学者建议不省略   driver.find_element_by_id   单数模式,返回符合条件的第一个 2:content-desc:
  content-desc 属性(可以唯一定位)   driver.find_element_by_accessibility_id 3:xpath:
  最常用的app定位元素方式(css是web专用app底层驱动并不识别css)css开发 渲染对应的样式(css前端开发基础)   xml,路径定位语言html父级是xml,xpath用来定位 xml 表示的元素和节点,手机里面元素结构树就是 xml结构,   driver.find_element_by_xpath

13:app中的xpath表达式解析

//*[@class="s-top-wrap s-isindex-wrap"]   
xpath表达式,浏览器自身具备解析 xpath 表达式的能力,解析xpath需要相当大的计算量
手机功能不具备解析 xpath 的能力,解析 xpath 没有在手机上执行
手机放弃解析 css 和 xpath 能力,除了手机浏览器有解析css和xpath的能力,
我们的 app 和 自动化测试工具 都不具备解析 xpath 能力
解析xpath放在 appium server 这块的,appium server解析xpath这个表达式定位元素的方法,
然后转化成 底层 对 测试驱动识别出的 元素定位信号,然后交给它来执行,底层驱动定位元素用的什么方法
它和平台自身用的测试框架有关系  安卓为例:安卓用的UiAutomator,用UiAutomator api进行定位

14:xpath语法

//元素类型(class属性)[@属性="属性值"]  根据 属性 来定位元素的,属性能够唯一定位元素,
//*[@text='科学']            假设属性唯一定位元素,元素类型可以*来代替      * 匹配所有元素类型    
class选择:
//class =//类型  这个属性类型在 appium 里面就是 class属性的值(appium中class的值当成元素类型)          //android.widget.TextView   ==  //*[@class="android.widget.TextView"]                        //android.widget.TextView     这里class当作标签了    //*[@class="android.widget.TextView"]    这里class当作属性     在app里面class既是属性也是标签   这两种写法效果一样的
xpath里面的层级关系:
  父元素/子元素
  祖先元素//后代元素
  第几个子元素,[x] 下标
  兄弟关系
xpath属性定位:
  通过id选择      ://*[@resource-id='com.zhihu.android:id/container_parent']
  通过class选择     ://android.widget.TextView
  通过其他属性选择    ://*[属性='属性值']
  选择子元素      ://*[属性='属性值']/*
  选择父元素      ://*[属性='属性值']/..

15:三种元素定位工具

一:元素定位工具一:appium server自带的 inspector
  appium 元素定位工具:本质是调用 appium 的 api 去操控这个元素,然后调用 appium 的截图方法查看手机页面内容
  从手机当前页面去截图然后和当前元素结构树做个匹配,才能使用元素定位工具
二:元素定位工具二:WEditor
  WEditor:python 结合 appium 底层框架研发的
  WEditor的使用步骤:
    1:安装:pip install weditor    python开发的,python安装weditor就行
    2:启动:python -m  weditor      一个python模块,这个动作就是:先把元素定位工具客户端给启动起来
    3:运行浏览器默认谷歌,
      python -m  weditor ,只是 本地打开 服务器,并且浏览器能够访问,手机和电脑需要能够连接
      WEditor工具原理:点击 connect 需要在手机上装个 ATX
      (ATX安装好了之后他会和和电脑端进行通信,数据交互,所有才能定位当前的界面)   ATX:小汽车,

1:WEditor工作特性:
  1:这个工具支持实时模式,需要手动点击激活,
    服务器不停向手机ATX发送请求,不停传画面,对性能有损耗,一般关掉,
  2:相对appium这个工具定位更精准,有时候手机界面不是纯原生的,
    定位元素要把界面html元素结构转化成原生的结构,有的工具有差距,转化能力没那么强
  3:这个工具可以直接提供xpath,和手写的类似,可以直接复制使用     只适用安卓
    但是不适合写脚本全部复制这些xpath来使用,获取多个元素的表达式的时候就不合适了
  4:还有个锁定坐标的功能,点击元素之后坐标可以锁定
    (坐标锁定之后是一个position百分比的坐标) x,y显示大的是像素坐标
  5:右侧可以写代码确认元素定位方法是否可以,这个api不是appium的,是ui2的--另外一套的方法,这个库只支持安卓

2:注意事项:
  1:ATX安装到你手机上--安装代理程序
  2:运行代码时候需要结束UIAUTOMATOR,这个工具和appium有冲突的,
  3:原理和appium一样,手机上安装一个代理程序ATX通过代理和本地进行通信,
    完成元素定位,截屏等(代理通信,传输数据),手机界面的屏幕数据传给服务端,服务端截获数据展现在浏览器上面
  4:WEditor 验证元素是不是存在和唯一存在得方法,在工具里面输入命令:如下,写python代码
      # coding: utf-8
      #
      import uiautomator2 as u2
      d = u2.connect()
      脚本输入的内容:
      ele=d.xpath('//*[@resource-id="com.hpbr.bosszhipin:id/tv_required_location"]')
      print(ele.text)
三:元素定位工具三:安卓sdk/tools/bin/uiautomatorviewer 工具
  uiautomatorviewer:E:\androidsdk\tools\bin\uiautomatorviewer里面,
  这个工具和 appium 有冲突,很脆弱,不需要占用任何端口,安卓原生的
  这个工具只能查看元素的特征,功能单一,截图更清楚,定位速度更快
  这个工具能将当前界面的截屏和元素信息保存下来
  左上角第四个图标save——>保存在桌面(然后桌面有两个文件:一张图+xml文件)
  弄出来有个问题。xml文件元素节点标签名都是node
  使用xpath表达式使用node作元素类型,写不写都一样---这时候采用class属性作为元素特征
四:元素定位方法
  1:查看 元素结构树
  2:appium自带的 元素定位工具
  3:代码来测试,自己跑(find-elements),然后打印元素列表的长度
  4:WEditor 输入python代码自己验证
五:实在上面三种元素定位工具都不得行,只能使用黑科技方法了
  需要根据xml结构树定位元素,元素属性去对比,去文本里查找
  利用driver.page_source  获取页面的xml信息,也就是页面的元素结构树,然后自己做元素定位
  (有些界面受保护的,界面进去是黑的,定位不了,比如boss直聘密码修改界面)

16:ui1和ui2

ui1驱动是原生的,可能兼容性问题小一点。
UiAutomator2   是appium优化后的安卓底层自动化驱动,性能更强

17:appium环境问题深入了解

driver=webdriver.Remote('http://localhost:4723/wd/hub',desired_caps) ,
  这行代码负责启动被测应用,这行代码负责检测很多事情,检测各种环境,电脑端的,jdk,安卓sdk,没有匹配好都能检测出来,
  手机端安卓的ui1和ui2和appium settings环境没有安装或者不能启动都能检测出来
webdriver.Remote(
'http://localhost:4723/wd/hub',desired_caps)这一行代码发生了什么?   1:客户端代码与appium server建立连接,并传递caps配置信息,运行代码之前先要启动appium server,启动服务脚本才能向它发送请求   2:Appiumserver接收到caps配置信息,检查配置信息是否符合要求,必要的配置项是不是都有,是不是格式错误   3:利用adb工具检查当前连接的移动设备,通过之后先看一下利用adb工具检查     appium根据你环境变量的安卓sdk路径自己去找adb,和cmd命令输入不同     配置项写了版本号不对,根据你设定的版本编号无法找到设备,     安卓10版本你写个8也会错误提示的,有时候也能找得到,会忽略版本信息   4:第一次运行脚本的时候安装appium-settings与uiautomator2到被测手机     默认使用ui2,大部分安卓机都支持ui2,性能更好,问题少   5:手机启动appium-settings作为监听服务,用于手机和appium-server进行通信     手机无法启动appium-settings也会提示错误,提示appium settings无法打开     这时候试试手动点击能不能打开,不能打开的话查看appium-settings和ui2的相关权限是否都打开   6:uiautomator2启动被测app,手机操作系统打开被测试应用,也要相关权限, uiautomator2可以定位h5界面的元素)小程序自动化

18:adb连接方式

1:usb有线连接   (adb devices -  查看) adb devices -l

2:无线连接:主要利用adb无线连接功能    1:先要 usb 有线连接到电脑上 --首先需要激活无线连接   2:激活手机Adb的无线服务,输入命令行:adb tcpip 5555(5555是手机默认的端口)   3:计算机以无线方式连接到手机:输入命令:adb connect <device ip>) ---当前手机的ip     手机上查看当前手机的ip,atx和各种方式都可以查看当前手机ip     如果是四个5激活的:adb connect <device ip>) 没有问题     是其他端口激活的:adb connect <device ip>):8888 连接时候需要带上端口号 使用无线连接的意义:   可能usb端口有限,或者要拿着手机到处走,有限不方便,使用无线需要确保手机和电脑处于同一局域网,连接同一个wifi 无线连接的优势:   1:减少USB连接干扰   2:提高连接稳定性   3:脱离有线的束缚,(确保在同一局域网(连接同一个wifi)) 无线连接的问题:无线连接采用的是adb连接,adb连接和手机上appium settigs不占用同一个端口,一般不会有冲突和干扰   1:不能激活   2:拔线后无线连接断开了,这种情况是系统自己内部逻辑--安全机制比较好,     发现没有连usb先就会断开所有的adb服务,避免安全问题,不能解决使用有线连接     无线连接问题(1:无线连接不能激活 2:无线连接拔线就断开了)华为,360等,     发现断开usb就断开所有adb连接服务

19:appium自动化原理

appium自动化主要靠手机底层驱动,自动化程序能在手机上面去运行,ui1或者ui2,
使用ui1输入中文需要加两个配置项,配置一下输入法,配置输入法可能手机安全等级比较高,禁止配置输入法--就gg了

20:一些常用的adb命令

adb kill -server                           关闭adbservre
adb devices                              启动adb server,检测
adb --help                                查看命令
adb install D:\全部环境\Chrome\gugeliulanqix64.rar        adb命令,安装包的命令,包拖动到cmd输入命令里面然后回车就行

21:手机界面的一些操作

appium能不能做游戏自动化
有些app控件不是应用的安卓原生的应用开发的,可能利用unit  3d'等技术开发的,
它的控件对于uiautomator而言是不可检测的,无法正常寻找元素和定位元素进行Zion规划
有时候应用出现这种情况,某个应用的某个界面上面的某个控件就是点击不了,
不是应用的安卓原生的应用开发的),这时候考虑使用其他方法,坐标操作(临时解决方法,某个元素定位不到的时候)

22:appium自动化一些常用的操作函数

一:坐标操作,模拟坐标点击:driver.tap(x,y)
  当某个元素定位不到的时候,可以采用坐标操作,可以模拟长按效果,默认点击
优点:可以暂时解决解决定位不到元素的问题,不能全部代码用坐标来写,坐标都是飘忽不定的,失败概率高 缺点:不能适用不同分辨率的屏幕 driver.tap([(
870,y)],3000)  
  传参列表嵌套元组:[(),(),(),(),()],每个元素代表一个坐标。列表里面最多传5个坐标,最多五根手指   对于我们这个普通的安卓应用而言,开发最多只实现三根手指的交互效果,通常写三个坐标差不多了,   写四个五个没其他特殊效果,最常见的就是一个数据   坐标点击,这种点击不会寻找元素,这种运行速度更快,这样操作点击可能经常需要等待 driver.tap([(
870,90)])     点击坐标 driver.tap([(870,90)],3000)   长按坐标3000ms,写时间参数就是长按的效果,长按时间就是你写的时间,时间单位是ms,毫秒 3000ms=3s(长按的位置不要有图标,否则效果不同)
二:相对位置模拟坐标点击
  可以坐标相对屏幕位置计算(减小误差)相对位置(宽高比例位置)
  使用2分法案来计算相对位置:高度一半位置肉眼分辨出来(获取1/2位置),
    1/2再取1/2,变成1/4,一直这样2分法制下去就能落到点击区域
    (或者获取坐标值相除也行)比较快速的方法
三:获取屏幕尺寸:  driver.get_window_size   
  size=driver.get_window_size()     获取屏幕尺寸,返回一个字典,宽和高是值
  width=size['width']             宽
  height=size['height']          高
  y=height/16               取相对坐标,点击的点在界面宽占1/16
四:滑动操作:    driver.swipe(x1,y1,x2,y2,1500)

driver.swipe(x1,y1,x2,y2)
  (api) x1 y1起点 x2 y2 终点
---- 起点和终点的坐标   明确起点和终点(向上或者向下滑动)手指落下的点起点,滑动完手指松开的点是终点
使用场景:目标元素不在当前屏幕范围,需要滑动屏幕 解决方法:模拟滑动   左右滑动,上下滑动 考虑起点和终点
--方向错了逻辑不同   起点在下面终点在---从下往上滑,下面的内容显示
driver.swipe(
500,1300,500,300)     横坐标不变,纵坐标向上滑动了1000个像素的距离 driver.swipe(500,1300,500,1300-1000)  也可以起点的y桌标-滑动的距离这样写 滑动的时候没有坐标让我们去等待去寻找的,如果进入应用就开始滑动,有页面跳转的话需要sleep硬编码sleep等待一下
滑动具有惯性:   从起点滑动到终点需要时间,时间没有设置,和设备底层有关系的,保险需要设置一下,   因为如果滑动距离很长,滑动时间很短,会造成惯性效果,不可控,一下滑很多   自动化的时候可以设置时间让这个滑动变得可控,设置时间1500ms,每次设置1.5s时间,保证滑动稳定
五:获取元素的尺寸
  block.size      返回一个  宽高的字典
六:模拟按键操作  :driver.press_keycode(Keycode)  
  使用场景:模拟手机硬件信息,如电源,音量,明暗,键盘等
  driver.press_keycode(Keycode)      和driver.keyevent()效果是一样的
  安卓 keycode查看网址:https://www.cnblogs.com/xiaowenshu/p/10012794.html
七:模拟打开通知栏  :driver.open_notifications()      不需要填写任何参数
  滑动可以用来打开通知栏:屏幕往下滑动,通知栏除了可以滑动打开,还可以模拟打开通知栏
使用场景:   需要查看通知,被测应用会发通知,打开通知栏后定位通知栏信息进行对比是否和预期相符合   操作完之后,接下来需要关闭通知栏
八:关闭通知栏:三种方法    driver.keyevent() 
  1:可以向上滑动
  2:打开手机左下角的返回虚拟按键进行点击
    (需要调出虚拟按钮--安卓系统接收三个键的信号的,虽然现在都是全面屏习惯上滑,)
  3:模拟发送返回按键 driver.keyevent(4)
九:driver.keyevent()
  这个函数可以模拟发送任何按键,如abcdecf和特殊字符,(银行金融类的密码键盘不能输入,只能点击键盘)
  虚拟键盘的字符有个文档的:安卓keycode查看网址:https://www.cnblogs.com/xiaowenshu/p/10012794.html
  driver.keyevent(4)          这个表示发送返回键 (左下角的返回按键)
  driver.keyevent(66)          回车键(enter) 模拟直接输入enter
  代码不能快于页面的反应速度,很容易造成页面和操作不同步,很多地方现需要强等:
  比如send_keys()后模拟回车driver.keyevent(66) 中间建议加等待时间  
txt文本也能作为元素定位。xpath元素特性描述一些
十:手机界面操作:滑动屏幕操作
  appium webdriver 的swipe方法(坐标和duration)
  直接查看,预计操作坐标
  先获取元素坐标,再分析操作坐标(再健壮)
  location=ele.location 左上角坐标,(dict:有 x,y)
  sizel=ele.size 宽高(dict:有width,height)

操作不可见元素:
  先滑动到其可见
  再操作该元素
获取全屏幕尺寸:   screesize
=driver.windiw_size()   screenW=screenSize('width')   screenW=screenSize('height')
十一:获取页面的html信息
  driver.page_source

23:class="android.widget.EditText"

EditText:手机元素类型一定要是 .EditText类型的才是输入框,其他的类型是输入不了的,无法send_keys 
EditText 可编辑的元素,只有这种类型才是输入框,其他类型的输入不了,操作一个按钮sendkeys是操作不了的,只有输入框才能sendkeys

24:appium-进阶扩展  

当前代码需求:
'''
自动化BOSS尝试自动化方式完成以下操作
  1.进入我的标签
  2.点击右上角设置图标
  3.进入账号与绑定
  4.进入设置密码
  5.完成密码设置
上面每次这样操作都需要主动自己登录才能执行测试用例:如下新需求   1:实现登录的动作   2:密码每一次修改都换变化,现在是写死的,执行完一次测试用例后再执行一下,密码需要手动还原回来,手动替换     需要每次执行都会自动替换密码,不需要每次手工去修改 ,使用配置文件方法,密码写在配置文件,     每次修改成功的话就把密码调换一下,下次登录用新的密码去登录,原来的密码做修改密码,调换过来再重新登录---一循环操作ok     不能保存在python文件里面,因为文件保存在中间这种方法,这个程序结束就没有了,下次再运行程序还是从原来开始的,还是没有改     要把文件写在相对当前程序来说不变的地方--配置文件(yaml格式)     密码需要手动一直换,怎么才能一直轮询替换代码,不需要手动换密码,     密码使用替换文件的办法,每次修改成功就密码调换一下,下次登录时候新密码登录,原来的密码下次更改     建议不保存再中间变量里面。程序结束如下就没有了,下次再运行还是从原来开始的,放在相当当前程序不变的地方     建议使用yaml文件来配置,定义各种格式方便,

25:yaml配置文件

yaml的意思其实是:
  yet another markup language (任是一种置标语言)缩写 yaml是专门用来写配置文件的语言,非常简洁强大,远比json格式方便   可以用yaml作为自动化测试框架的配置文件或者用例文件 一:yaml环境搭建:   pip install PyYaml   PyYaml是python的一个专门针对yaml文件操作的模块,使用简单 二:yaml格式的读取   读取操作:需要yaml格式的配置文件  config.yaml或者congfig.yml都可以   congfig.yml   psw:     new: boss666     old: boss123   这种类似是字典格式,层级缩进在yml里面默认两个空格,格式要统一,   读取yaml文件操作:从yml文件读取密码,返回字典psw后面的值     res
=yaml.safe_load(open('conf.yml').read()) #load里面放读文件的方法     print(res['psw']['old']) 三:写yaml操作:   data={'psw': {'new': 'ywt666666', 'old': 'ywt888888'}}       #这是要写的内容,写到yaml里面   yaml.safe_dump(data,open('conf.yml','w'))            #dump里面第一个参数要写入的数据,第二个参数是打开的文件,                                     #open('conf.yal','w')让文件处于一种写的模式w, yaml读文件函数:   def get_psw():              #封装方法,配置文件yaml里面读取密码,返回内容返回内容的字典     with open('conf.yml') as f:     res=yaml.safe_load(f.read())    #safe_load安全读取,     return res['psw'] 写文件函数: #写入新的数据覆盖到yml文件   def write_psw(data):            #修改之后的密码,新密码,旧密码写入文件中     with open('conf.yml','w') as f:     yaml.safe_dump(data,f)

26:多终端测试    同时测试多台设备 ---移动应用测试

一:多终端的测试的理论:
  1:session通信机制:
    会话
    用于隔离通信干扰
    appium测试程序和appium server之间的http请求都必须在一个session种进行
    session id
    测试代码和appium server是通过session进行连接的
    代码需要和appium服务进行通信--session是为了保证多个客户端和同一个服务端进行通信,服务端知道消息是从那个客户端发过来的
    appium server同一时间只能接收一个代码发过来的请求,多终端测试,一个appium server无法完成,两台设备需要同时打开两个apium server

  2:appium server的特点
    一个server同一时间只支持一个客户端测试
  
3:测试代码一份就好(做好多终端的处理),一个appium server连接一个手机(这样的架构),     appium server可以不放在自己主机上,appium也可以放到其他主机上面     里面有专门的服务器跑自动化案例,测试代码可以在本机上,跑自动化时候appium在专门服务器上     appium架构逻辑cs 这里启动部分webdriver.Remote('http://localhost:4723/wd/hub',boss_caps)     的ip和port可以写成启动appium server服务和监听的服务器的ip和端口
二:多终端测试的前置步骤
  1:同时测多台设备,appium server要启动多个,
    注意:appium server启动的时候port 端口设置不能相同,默认4723
    同一端口无法同时打开appium server多次,再打开报错,因为端口已经被监听,再打开提示端口号被占用了
    改的端口号不能按照顺序改,appium server内部还有端口,advanced:Bootstrap Port-4724,
    这个端口用于和手机的Bootstrap Port相连的,appim server和手机通信的
    因为手机要向appium server 发送请求,appim server也需要在系统上面去启动端口号监听手机发送过来的请求,
    交互是双向的,所以端口占用不能再使用了
    多终端多个appium server端口号要+2 :4723  4725  4727,增加一个appium server端口号加2,隔一个

  2:准备好caps配置项信息,执行两台手机配置项不同,准备配置项文件,
    由于存在多台设备,所以需要指定设备名称用于区分
    ui2驱动和本机的通信端口需要设置一下,防止端口冲突,我们可以分别为待测试脚本添加配置项:systemPort
    底层驱动ui2需要和电脑本机通信默认端口号8200,如果不指定,单跑一台没问题
    同时运行多台设备会造成一台运行失败的情况,都用这个端口,另外一个设备连接这个端口会端口冲突
    需要指定端口号:配置项名称systemPort,
    自定义端口范围:8245-8299(也就是最多可以测试55台手机)
    安卓8设备:
      android8['deviceName']="xiaomitest"
      android8["systemPort"]="8251"
    安卓9设备
      android8['deviceName']="meizhumitest"
      android8["systemPort"]="8252"
    由于多台设备,必须区分设备名称,ui2驱动和本地的通信端口也需要设置一下,防止端口冲突,需要配置systemPort

  3:使用框架pytest的xdist插件实现代码并行的功能
    代码测试时候同时进行,不用框架的话需要编写多线程的代码,或者多进程代码
    python里面编写多进程,python多线程无法充分利用多核,多进程编写代码比较麻烦
    所以这时候使用框架pytest的xdist插件可以实现并行的功能,pip install pytest-xdist  (xdist  pytest并行插件)  
    1:pyest参数化:给初始化加上启动参数     
      @pytest.fixture(params=[("4723",android8),("4725",android9)])
      def before_test(request):
        pass
    2:pytest并行插件xdist
      pip install pytest-xdist  
      -n 2表示用两个进程启动测试脚本,因为该测试有两组参数
      执行命令:pytest.main(["boss_自动登录0916.py","-s","-n 2",'--alluredir=tmp/my_allure_results'])

这三步骤都没问题就可以进行并行测试了

27:boss直聘代码演示测试用例

# 代码版本一:封装成函数级别,测试一台手机的代码版本
import time
from appium import webdriver
from config import android8, android9, xiaomi_boss_caps  # 导入多终端需要的两款手机的配置
import pytest
import yaml
import os


# 函数进行封装,函数具体描述你做什么事情的,整个代码都是修改密码
# change psw的函数
# 封装函数需要名副其实,只干符合你函数名称的事情,,
# 任何与你无关的事情都不需要去做,不要封装,如果有其他的定义另外的函数进行

# 初始化driver对象-用于控制手机
def start_app():
    global driver  # 声明driver全局变量
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', xiaomi_boss_caps)
    # 隐士等待的触发条件是?--获取元素,且当前页面没有该元素才会触发,
    driver.implicitly_wait(10)  # 稳定元素,隐式等待


def end_app():
    global driver
    driver.quit()


def change_psw(old_psw, new_psw):
    global driver
    # 1.进入我的标签
    # 假设当前处于登录状态
    driver.find_element_by_id('com.hpbr.bosszhipin:id/iv_tab_4').click()

    # 2.点击右上角设置图标
    driver.find_element_by_id('com.hpbr.bosszhipin:id/iv_general_settings').click()
    # 3.进入账号与绑定
    driver.find_element_by_id('com.hpbr.bosszhipin:id/cl_item').click()
    # 4.进入设置密码
    driver.find_element_by_xpath('//*[@text="修改密码"]').click()
    # 5.完成密码设置
    # time.sleep(2)
    # print(driver.page_source)
    # 5.1输入旧密码
    driver.find_element_by_id("com.hpbr.bosszhipin:id/et_old").send_keys(old_psw)
    # 5.2输入新密码
    driver.find_element_by_id("com.hpbr.bosszhipin:id/et_new").send_keys(new_psw)
    # 5.3确认密码
    driver.find_element_by_id("com.hpbr.bosszhipin:id/et_new_confirm").send_keys(new_psw)
    # 5.4点击修改密码
    driver.find_element_by_id("com.hpbr.bosszhipin:id/btn_save").click()


# 检查是否修改成功,修改成功了会跳转到登录页面,登录页面的事情
def check_modify_psw(expect):
    global driver
    # 检查是否修改成功,判断是否到首页了
    target = driver.find_element_by_id('com.hpbr.bosszhipin:id/tv_password_login')
    assert target.text == expect


def login_boss(psw):  # boss app账号主动回填的(账号有登录记录),直接写密码
    global driver
    # 1点击账号密码登录
    driver.find_element_by_id('com.hpbr.bosszhipin:id/tv_password_login').click()
    # 2:输入密码
    driver.find_element_by_id('com.hpbr.bosszhipin:id/et_password').send_keys(psw)
    # 3:点击登录
    driver.find_element_by_id('com.hpbr.bosszhipin:id/btn_login').click()


# 从配置yaml文件读取密码
def get_psw():  # 封装方法,配置文件yaml里面读取密码,返回内容返回内容的字典
    with open('conf.yml') as f:
        res = yaml.safe_load(f.read())  # safe_load安全读取,
    return res['psw']


# 将修改后的密码存入到yaml配置文件
# 写入新的数据覆盖到yml文件
def write_psw(new, old):  # 修改之后的密码,新密码,旧密码写入文件种
    data = {'psw': {'new': old, 'old': new}}  # 新密码等于旧密码,旧的密码换成新的密码
    with open('conf.yml', 'w') as f:
        yaml.safe_dump(data, f)


# -------------------------------------上面是测试库,下面是测试用例------------------------------------------------------------------------


def setup():  # 这是函数级别的,每执行一个测试用例的时候这个函数和teardown都会执行一遍
    start_app()


def teardown():
    end_app()  # 这样写就算下面的test_modify_psw执行遇到问题也会执行teardown,end appp,app也会退出,因为使用了测试框架


# 测试用例代码部分,登录之前需要获取密码
def test_modify_psw():  # 修改密码测试用例
    psw = get_psw()  # 获取密码
    login_boss(psw['old'])  # 登录的时候使用的旧的密码(当前密码old_psw)
    change_psw(psw['old'], psw['new'])  # 先传以前的旧密码,后面传新设置的密码
    check_modify_psw('账户密码登录')

    write_psw(psw['new'], psw['old'])


if __name__ == '__main__':
    pytest.main(['auto_boss_ywt11-1.py', '-s'])
代码版本二:封装成函数级别,多终端版本
    多终端测试多个手机,需要两个boss直聘的账号,密码需要设置一样,然后执行下面这段代码
import time
from appium import webdriver
from config import android8, android9, xiaomi_boss_caps, android10  # 导入多终端需要的两款手机的配置
import pytest
import yaml
import os


def start_app(port, caps):
    global driver
    driver = webdriver.Remote(f'http://127.0.0.1:{port}/wd/hub', caps)  # 需要测试多台手机,每个手机需要的参数和端口不同,需要配置
    driver.implicitly_wait(10)


def end_app():
    global driver
    driver.quit()


def change_psw(old_psw, new_psw):
    global driver
    # 1.进入我的标签
    # 假设当前处于登录状态
    driver.find_element_by_id('com.hpbr.bosszhipin:id/iv_tab_4').click()

    # 2.点击右上角设置图标
    driver.find_element_by_id('com.hpbr.bosszhipin:id/iv_general_settings').click()
    # 3.进入账号与绑定
    driver.find_element_by_id('com.hpbr.bosszhipin:id/cl_item').click()
    # 4.进入设置密码
    driver.find_element_by_xpath('//*[@text="修改密码"]').click()
    # 5.完成密码设置
    # time.sleep(2)
    # print(driver.page_source)
    # 5.1输入旧密码
    driver.find_element_by_id("com.hpbr.bosszhipin:id/et_old").send_keys(old_psw)
    # 5.2输入新密码
    driver.find_element_by_id("com.hpbr.bosszhipin:id/et_new").send_keys(new_psw)
    # 5.3确认密码
    driver.find_element_by_id("com.hpbr.bosszhipin:id/et_new_confirm").send_keys(new_psw)
    # 5.4点击修改密码
    driver.find_element_by_id("com.hpbr.bosszhipin:id/btn_save").click()


def check_modify_psw(expect):
    global driver
    # 检查是否修改成功,判断是否到首页了
    target = driver.find_element_by_id('com.hpbr.bosszhipin:id/tv_password_login')
    assert target.text == expect


def login_boss(psw):  # boss账号回填的,直接写密码
    global driver
    # 1点击账号密码登录
    driver.find_element_by_id('com.hpbr.bosszhipin:id/tv_password_login').click()
    # 2:输入密码
    driver.find_element_by_id('com.hpbr.bosszhipin:id/et_password').send_keys(psw)
    # 3:等级登录
    driver.find_element_by_id('com.hpbr.bosszhipin:id/btn_login').click()


# 从配置yaml文件读取密码
def get_psw():  # 封装方法,配置文件yaml里面读取密码,返回内容返回内容的字典
    with open('conf.yml') as f:
        res = yaml.safe_load(f.read())  # safe_load安全读取,
    return res['psw']


# 将修改后的密码存入到yaml配置文件
# 写入新的数据覆盖到yml文件
def write_psw(new, old):     

                 # 修改之后的密码,新密码,旧密码写入文件中
data = {'psw': {'new': old, 'old': new}}  # 新密码等于旧密码,旧的密码换成新的密码
with open('conf.yml', 'w') as f:
    yaml.safe_dump(data, f)


# -------------------------------------上面是测试库,下面是测试用例------------------------------------------------------------------------

@pytest.fixture(params=[(4723, android10), (4727, android9)])  # 初始化函数的参数化,每个测试用例执行的时候都需要启动配置项
def before_test(request):  # 这是函数级别的,每执行一个测试用例的时候这个函数和teardown都会执行一遍
    port = request.param[0]
    caps = request.param[1]
    start_app(port, caps)
    yield
    after_test()


def after_test():
    end_app()  # 这样写就算下面的test_modify_psw执行遇到问题也会执行teardown,end appp,app也会退出,因为使用了测试框架


# 测试用例代码部分,登录之前需要获取密码
@pytest.mark.usefixtures('before_test')
def test_modify_psw():  # 修改密码测试用例
    psw = get_psw()  # 获取密码
    login_boss(psw['old'])  # 登录的时候使用的旧的密码(当前密码old_psw)
    change_psw(psw['old'], psw['new'])  # 先传以前的旧密码,后面传新设置的密码
    check_modify_psw('账户密码登录')

    write_psw(psw['new'], psw['old'])


if __name__ == '__main__':
    pytest.main(['auto_boss_ywt11-1.py', '-s'])  # 这样执行多终端测试用例,会串行执行,一台手机上执行测试用例完了后再执行另外一台手机
    pytest.main(['auto_boss_ywt11-1.py', '-s', '-n 2'])  # 加个'-n 2',两个线程并行运行,两台手机上同步执行
    # 2应该理解为两组数据,两组数据和被测设备进行匹配的,匹配不同的手机
    # "-n 2"参数,因为before_test为两组参数初始化,并行两个线程并行用-n 2,2两组数据,和被测试设备匹配的

28:appium可自动化的app类型

一:原生 :boss等app都是原生应用的自动化思路,元素定位工具打开,元素定位,然后操作元素这一套,这种方法都是针对原生应用
二:混合 :
三:web应用(手机网页):

29:原生应用

从开发角度考虑:原生应用(boss直聘等app),开发技术手机所有控件都是采用 系统原生控件做成的(更改内容需要修改源代码)

30:混合应用

美团app,购物页面等
混合应用的特点:原生的壳套了个网页,更新网页内容(外卖商家,上的一些商品需要更新),
并不需要更新客户端,只需要服务端更新数据,网页重新生成就行了,这就是混合应用的特点   (外卖商家)混合了原生和web应用(web应用通俗点来说手机网页),
比如浏览器打开百度,百度就是一个应用,应用部署在服务器上,通过浏览器展示给我们 这种混合应用架构一般是c/s架构(需要和服务器交互,混合应用里面嵌套了网页,
网页需要向服务器请求数据来展示)混合是cs架构,web应用是bs架构 服务器-客户机,即Client-Server(C/S)结构。
  C/S结构通常采取两层结构。服务器负责数据的管理,客户机负责完成与用户的交互任务

31:web应用:手机网页 

手机浏览器打开百度网页 b/s架构(b浏览器 s服务器)
bs/ 是WEB兴起后的一种网络结构模式,WEB浏览器是客户端最主要的应用软件。
这种模式统一了客户端,将系统功能实现的核心部分集中到服务器上,
简化了系统的开发、维护和使用。客户机上只要安装一个浏览器(Browser英 ['braʊzə]美 ['braʊzɚ]),
如Netscape Navigator或Internet Explorer,
服务器安装SQL Server、Oracle、MYSQL等数据库。浏览器通过Web Server 同数据库进行数据交 

32:电脑的Chrome浏览器可以调试手机网页

F12——>点击谷歌浏览器左上角小手机图标 ——>再刷新一下页面——>
谷歌浏览器上面有各种手机模式去选择,就是不同的分辨率的选择---调试页面  edit可以自己设计机型(自定义)
原理是:
  服务器以请求头区分客户端的类型,请求头不同

33:自动化手机模式这种网页

电脑浏览器 就能打开手机端这种网页的话,可以直接电脑 selenium 去打开就行,浏览器模式调整成 手机模式
selenium 以手机模式打开浏览器,pc上自动化手机模式网页(和appium没有关系),直接电脑跑就行,电脑上自动化手机相关的网页:
场景一:pc上自动化手机网页模式:pc电脑端用手机web模式打开网页测试   直接使用selenium就行,效率更快,不用通过appium
from selenium import webdriver

# driver=webdriver.Chrome()              #  初始化webdriver这样的那么默认打开浏览器就是pc模式,所以需要加配置---这种写法不可行

# 假设我们自动化谷歌浏览器,定义谷歌浏览器配置项(为了操作简单):
chrome_options = webdriver.ChromeOptions()


# 配置项里添加配置
# 1:设置浏览器手机模式,mobileEmulation 设置浏览器手机模式, 选择一种存在的模拟浏览器手机类型
# deviceName后面的机型谷歌浏览器里面要有的(默认的),自定义不要写上去, # 自动化的时候不会读取自定义的设置的信息(只读取默认显示的配置项),自定义的信息写在了用户信息里面,可能读取不到 chrome_options.add_experimental_option("mobileEmulation", {"deviceName": "iPhone X"}) # 配置项.add_experimental_option--增加配置 # mobileEmulation手机模式,后面一个字典,deviceName,iPhone X--手机类型 # 初始化webdriver时候增加配置项 driver = webdriver.Chrome(chrome_options=chrome_options) # 还有其他方法,自己学习 driver.implicitly_wait(10) driver.get('https://www.baidu.com/') # 自动化百度松勤 driver.find_element_by_id("index-kw").send_keys("松勤\n") # 电脑上\n有效的,点击,手机上无效,少了点击百度这一步 # 查看第一个搜索结果 res = driver.find_element_by_css_selector(".c-title-text").text print(res) assert "松勤软件测试" in res driver.quit()
场景二:手机上用手机浏览器自动化手机 web 页面  另外场景:appium的那一套
  手机上自动化手机网页模式(手机需要appium)
  需要手机自动化浏览器:手机的chrome浏览器
  配置信息添加:‘browerName’:‘chrome’就行,来代替包名和入口信息,无需再指定app包名和入口信息
  使用其他浏览器(前提有对应的驱动),然后自动获取package以及activity,自行切换webwiew
from appium import webdriver
# 定义配置信息    #这个配置信息,区别是被测应用变成了 浏览器:这时候有个问题:如果我们在手机上 自动化浏览器,那我们这个浏览器选择什么样类型的
# 自动化 浏览器需要驱动,自动化手机上面的浏览器也需要驱动:手机自带的浏览器没有提供开发测试人员使用的驱动,常用的浏览器还是chrome(手机版chrome浏览器)
# 谷歌浏览器和安卓系统同一家公司,谷歌浏览器自动化效果稳定 - -
# 自动化谷歌浏览器配置项信息很简单 - -不需要写包名和入口信息,直接配置项添加

# "browserName": "Chrome"
# 来代替包名和入口信息就行
# 手机上使用谷歌浏览器自动化网页时候,不做任何配置时候用的appium自带的谷歌驱动(不是电脑环境变量配置的chromedriver),
# appium使用默认手机谷歌浏览器驱动71 - 75,(appium自带的chromedriver2.46版本的,只支持71 - 75版本的chrome,
# 如果你手机上使用的不是71 - 75的chrome版本需要加配置,使用指定的浏览器驱动:+  # "chromedriverExucutableDir":"谷歌浏览器驱动目录")

caps = {
    'platformName': 'Android',
    'plathformVersion': '10',  # 设备的名称--值可以随便写

    'deviceName': 'test0106',
    # 提供被测app的信息-包名,入口信息:(被测应用是手机浏览器,自动化手机上面的浏览器也需要驱动)
    # 自带的浏览器没有提供开发测试浏览器使用的驱动,使用自动化常用的谷歌浏览器
    # 使用chorme浏览器配置信息添加 "browserName":"Chrome"不需要指定app包名和入口信息(配置简单)
    # appium使用默认手机谷歌浏览器驱动71-75,不做任何配置用的appium自带的谷歌驱动,需要加配置信息
    # 需要加配置信息指定浏览器驱动版本
    "browserName": "Chrome",
    'noReset': True,
    'newCommandTimeout': 6000,
    'automationName': 'UiAutomator1',
    'unicodeKeyboard': True,  # 修改手机的输入法,UI2不需要设置
    'resetKeyboard': True  # 自动化结束之后将输入法还原-概率性

    # 使用指定的谷歌驱动自动化浏览器---指定浏览器驱动chromedriver所在的目录,使用文件的绝对路径()
    # 谷歌浏览器手机和pc驱动版本都是一模一样的
    # "chromedriverExucutableDir":"谷歌浏览器驱动目录"
    # 'chromedriverExecutableDir':'C:\Tools\webdriver\chromedriver_win32_v81'   #只传了文件目录,没到.exe文件
}

# 接下来操作一样了
# 打开webdriver
driver = webdriver.Remote('http://localhost:4723/wd/hub', caps)
driver.implicitly_wait(10)

# 手机浏览器自动化百度搜索松勤
driver.get('https://www.baidu.com/')

# 自动化百度松勤
driver.find_element_by_id("index-kw").send_keys("松勤\n")  # 电脑上\n有效的,点击(只在app原生控件无效,手机浏览器有效)
# 查看第一个搜索结果
res = driver.find_element_by_css_selector(".c-title-text").text
print(res)

assert "松勤软件测试" in res
driver.quit()

driver.find_element_by_id("index-kw").send_keys("松勤\n")
# 这个\n在手机浏览器和pc浏览器都是有效的,但是在手机原生控件无效的
pc上浏览器和 手机上用chrome浏览器自动化web页面后面的代码一模一样,copy就行,减少一部分工作量,不需要修改一行,核心逻辑开发一份就行

自动化web页面有两种形式:平台不一样:   
1:pc 不需要appium,和手机无关,只要selenium   2:手机自动化浏览器 需要appium,需要用到手机
场景三:自动化嵌套在app内部的网页---hybrid app
  混合应用一般能在浏览器(电脑和手机上打开),
  假如自动化混合应用只能在手机应用打开(浏览器打不开),嵌套和特殊网页,浏览器无法打开小程序地址,
  这时候需要:连着混和 应用直接去测试,自动化嵌套在app内部的页面(hybrid app)   这种场景是 混合应用的测试

混合应用的架构:
  混合应用 里面嵌套的 网页实现技术和逻辑都和我们 pc和手机上的浏览器页一样,都是html,css,js
  pc上的网页,服务器返回过来的是html代码,我们看到都是很直接的图像界面和结构,
  需要靠电脑浏览器隐形或者解释器渲染成的,靠浏览器内核(核心)渲染成的) 
  国产浏览器没有自己的内核,浏览器内核借助其他的
  浏览器内核就相当于浏览器下面对html,js,css一个解释器和编译器, 把内容编译之后展示到浏览器上面,对容错率,性能很高的要求)
  开发浏览器内核是体现一个国家it综合实力的,(
  pc浏览器和手机浏览器都自带 内核去渲染页面的,如果没有装chrome,混合应用里面内容显示不了(应用没有带渲染的能力))(京东很多都是混合应用)   混合应用里面的网页
---靠操作系统内部的一个webview解释器这个功能进行渲染的,----靠手机自带的webview解释,渲染过程很复杂   内部的的混合应用的这个解释器也简称为:webview     手机设置可以搜索webview是那个版本的,手机的webview可以看成是chrome浏览器,混合应用的内容就靠这个浏览器的核心去解释,去渲染,     安卓webview本质上是chrome浏览器内核,因此版本和chrome   了解webview     混合(hybrid)应用:一部分是原生界面和代码,另外一部分是嵌套网页     比如微信,支付宝,     内嵌了一个浏览器内核,由浏览器内核实现的 混合应用的 内嵌 的 展示 页面 内容的模块称之为webview(webkit.webview) 负责解析app内嵌网页的部分是手机系统内置的 webview 安卓webview本质上是chrome浏览器内核,因此版本和chrome一致 webview理解为一个chrome浏览器,混合应用的内容靠这个浏览器核心解释渲染混合应用里面的元素结构树 weditor比较强大:
  解析内容比较全,能够把html元素以 原生控件形式 展示解析出来,解析出来的元素类型都是 view.view 类型的,
  除了可以点击的按钮类型会有点不同(webkit.webview后面都显示的内容),追根溯源是因为上面有webkit.webview标签,
  如果查看元素结构树时候发现有webkit.webview,那么百分百是一个混合应用,   这一块内容就是网页, 混合应用负责展示网页的部分是webview,webview控件负责展示网页   负责渲染网页内容的是手机里面自带的webview实现(自动化时候选择驱动选择匹配手机webview类型的)思路 什么时候使用原生方式,什么时候使用混合方法   需要先判断那种类型的应用,一般情况下以原生的方案进行自动化的,如果元素定位定位时候发现里面是webkit.webview
  那么这一块的内容就需要用混合应用了,用自动化web或者自动化混合应用的形式
  到底用那种方案,如果网页内容能够浏览器打开
--通过浏览器自动化效果最好,如果这一块内容通过浏览器打不开,那只能连着整个应用进行自动化了, 苹果的webview是另外的核心的,比如说苹果自己的浏览器是safari的,评估的核心就是safari的核心,   h5定位使用的另外的方法,之间使用定位工具定位     自动化混合应用前提条件:   1:通过电脑浏览器手机浏览器无法打开自动化webview对应页面的时候,   2:采用这种备选方案,直接自动化整个混合应用来自动化app内部webview(混合应用里面的内嵌网页)
自动化混合应用的准备条件:
一:准备工作1:开启被测应用的debug模式
  webview(混合应用的内嵌网页)不是你想自动化就自动化的,webview(混合应用里面的内嵌网页)可能有些核心内容不想让别人看到,
  这时候需要把 webview 应用(混合应用的内嵌的展示页面内容的模块称之为 webview)的 debug 开启,
  目前测试都属于黑盒测试--不改变应用的内容(只针对它做输入输出)
  (目前为了对webview做测试需要改变web view的源码)---假设被测应用的 webview 发布测试版本,
  测试版本需要和开发沟通一下,打开 webviewdebug 模式,这是前提条件:
  如何打开webview的debug模式:只要webview对象加个 setwebcontentsdebuggingenable就可以了
---和安卓开发沟通     加一下这行代码,debug模式打开,这一个搞不定无法自动化webview     app修改编译       对webview对象加入setwebcontentsdebuggingenable调用       protected void onCreate(Bundle savedInstanceState){         super.onCreate(savedInstanceState);         webview my webview =(webview)findviewvyId(R.id.jcywebview);         mywebview.setWebContentsDebuggingEnable(true);         }   从应用市场下载的app不具备此条件(debug都是关闭的)(不开启debug无法自动化混合应用)     wv_test.apk 练习的app----开启了debug模式的app     如果公司内部自动化webview---一定要开启webview,否则无法自动化混合应用的方案自动化webviiew
二:准备工作2:
  前面浏览器无法打开被测应用的网页(那么无法调试:
    使用谷歌浏览器有个 inspect 功能可以打开---确保手机通过adb连上了电脑,
    (地址栏输入:chrome://inspect--还需要电脑具备FQ ---打开网页后点击一下页面下方的:inspect,可能会白屏
    手机 webview 版本的问题,手机 webview 版本不能低于百分之79,)

  webview的内容依赖所在的app:
    通过chrome的 远程调试功能---需要FQ
    打开chrome浏览器,地址栏输入:chrome://inspect
    需要android4.4或者更高版本

  js和css写在一起混合了

上面就是查看 webview 内部元素的一个方法,如果页面能够通过浏览器打开,就直接通过浏览器打开去调试,
  如果不能通过浏览器打开页面那么通过chrome的inspect功能(远程调试webview界面)
  这个工具使用时候需要远程访问谷歌浏览器(解释webview工作需要的资源很多,这一块工具放在谷歌远程服务器上
  所以需要具备访问谷歌服务器的功能,FQ绕不开)
三:准备工作3:选择合适的webdriver:
  选择合适的webdriver:选择谷歌浏览器驱动去驱动webview(手机系统的webview可以看成chrome浏览器)去显示
  (首先知道自己手机的webview是什么版本的,匹配好版本
  手机设置:搜索一下 webview--然后点击进去查看一下webview版本
  匹配版本对应关系和chrome浏览器一模一样的,---webview当成chrome浏览器就行,浏览器核心都是一样的
    webviwe版本就是chrome的版本,匹配对应的谷歌驱动(匹配版本对应关系和浏览器一模一样的,
    webview---当成一个浏览器,浏览器核心都是一样,每次更新就是更新浏览器核心)
webview的自动化:
  webview的内容不依赖所在的app(可以直接电脑浏览器和手机浏览器打开url) 
    只是打开一个url
    直接用chrome浏览器打开对应网页
    使用手机模式
  appium自动化webview:
    appium中把所有的界面称之为 context
    native 部分的 context 名字一般为 NATIVE_APP
    webview部分的 context 则为 WEBVIEW_XXXX(对应app package名)

我们怎么查看当前有哪些context:driver.contexts
显示当前的context的则是:driver.curren_context
切换context:driver.swicth_to.context(contextname)
appium架构逻辑:
  当前处于NATIVE_APP,NATIVE_APP是界面环境,appium中把所有的界面环境称之为context,
  NATIVE_APP是 原生部分,我们默认进入手机 app 里面,默认处于 原生界面环境
  如果我们想要以自动化网页形式自动化 webview 里面内容,需要切换到 webview 部分的 context 界面环境,

webview部分的context名字:WEBVIEW_XXXX(对应app package名)
  可以打印 context,:driver.contexts 查看所有的context,也会把其他的context给打印出来
  ,有时候打开手机浏览器也会打印出手机浏览器的context(界面环境)
  而显示当前的context的则是(查看当前处于哪一个界面):driver.curren_context
  切换到对应的webview context中:driver.swicth_to.context(contextname)

34:代码实例:myHybirdApp(混合应用自动化),自动化豆瓣-搜索肖申克的救赎,查看电影评分

    #自动化混合app
    from appium import webdriver
    from config import hybird_caps
                                                                                    #自动化豆瓣-搜索肖申克的救赎,查看电影评分

                                                                                    #启动webdriver,初始化webdriver
    driver=webdriver.Remote('http://localhost:4723/wd/hub',hybird_caps)
    driver.implicitly_wait(10)

    #混合应用里面输入网址--豆瓣网址
    driver.find_element_by_id('com.example.haiwen.myhybirdapp:id/editText').send_keys('https://m.douban.com/home_guide')           
    #点击enter开始访问
    driver.find_element_by_id('com.example.haiwen.myhybirdapp:id/button').click()                                                  


    ----------上面的点击元素操作都是都是操作 安卓原生应用的,还没涉及到网页,待会网页里面才是操作网页的    
        
    ---------- 开始像浏览器一样操作 网页内容    ,网页里面找元素和调试元素可以在谷歌浏览器下面用手机模式打开网页进行调试
    #查看app所有contexts
    print(driver.contexts)                   ['NATIVE_APP'--原生的界面信息, 'WEBVIEW_com.example.haiwen.myhybirdapp'---app的webview界面信息]
                                            一般情况下一个应用只有一个webview(界面信息),百分之99一个混合应用里面只有一个webview,
    
    print(driver.current_context)                    #查看当前处于哪个context    NATIVE_APP

    #切到对应的 webiew context--切成功后就可以像 浏览器一样操作网页内容了,只有成功切到webview里面才可以像自动化手机浏览器一样操作webview里面的内容
    # driver.find_element_by_css_selector--因为这种css选择元素自动化网页才有的语法,
    为什么appiumdriver可以调用find_element_by_css_selector--appium是在selenium基础上开发的,依赖selenium1的--

    driver.switch_to.context('WEBVIEW_com.example.haiwen.myhybirdapp')
        
    #切换成功后当前打印当前的context
    print('当前的context是:%s'%driver.current_context)    

    #搜索电影名称
    driver.find_element_by_css_selector('input[class="search-input"]').send_keys('肖申克的救赎\n')  #网页界面能直接\n回车
    #查看电影分数
    rate=driver.find_element_by_css_selector('li.search-module:nth-child(1).search_results_subjects li:nth-child(1) .rating>span:nth-child(2)').text
    print(f'电影评分是{rate}')
    
    #切回原生app,重新百度应用,搜索松勤

    driver.quit()
    #主流的是安卓的webview和ios webview(安卓的webview核心是谷歌的内核)


    如果操作完了后还想接着去搜索其他电影需要切回原生应用界面再去操作,循环这样做    
    driver.switch_to.context('NATIVE_APP')

35:总结

1:在电脑上自动化手机页面不需要 appium
2:用手机浏览器自动化需要 匹配手机浏览器版本的驱动
3:只有开启了 debug 模式的 app 才能被自动化 webview(webkit-webview里面的内容)内容
4:自动化webview界面内容需要匹配webview版本的驱动
  自动化对象            测试工具         驱动参考
  电脑端pc              selenium       pc电脑端 chrome浏览器
  手机端网页            appium      手机浏览器
  混合app-webview         appium        手机系统webviewWebView 是术语,是指网页视图
  三个场景:
    场景一:电脑上自动化 web页面--不需要 appium ,测试工具使用 selenium所用的驱动参考pc端浏览器版本
    场景二:手机浏览器自动化 web页面--驱动匹配需要看手机浏览器,默认手机版本chrome浏览器,自动化手机网页,自动化工具appium,测试平台是手机-
    场景三:自动化混合应用:驱动参考手机系统的 webview,重点区分一下
  h5就是混合应用,就是混合应用里面 嵌套的网页
  自动化混合应用的时候只有开启 webview debug 模式的混合应用才能被自动化webview里面的内容,如果没有开启---两种办法:
     1:找开发让他开启debug
     2:不开debug,那么使用自动化 原生应用方案的思路去自动化应用weditor等定位原生 app界面的工具,
      虽然你嵌套了 html 网页代码,但是把你界面当成 原生页面用 三种定位工具进行定位,
      但是可能 不精确,不是所有的这个元素结构都能解析出来的,肯定没有用网页方法来的清晰,丢失精度---没办法的办法,
  定位 浏览器打不开的 web页面 只能手机原生界面打开的网页的情况:
    需要使用chrome inspect模式定位:
    1:一定需要FQ   搞技术的都需要,稳定需要付费

36:appium所有学习知识总结:

1介绍appium工作原理环境搭建(了解工作原理能定位问题,才有解决思路)(了解原理应付面试)(appium干什么的,什么作用)
2:元素定位方法(appium一个三种元素定位方法:id,content-desc,xpath)--什么情况下使用 
  元素定位工具三种:
  1:appium自带的,
  2:sdk自带的(不稳定,依赖环境太深,不好用),
  3:weditor(需要手机安装小黄车插件))

3:手机界面操作+测试用例框架的集合 
  手机界面操作:(坐标点击,滑动,模拟发送键盘事件,(模拟按键没办法模拟情况下事情)) 
  测试用例和框架结合(pytest框架+allure测试报告生成) 

4多终端的测试思路与实践(进阶)
5:混合应用(范围很广)H5:最常用的,微信工作号,小程序都是混合应用(课程录播放) 
  (微信小程序比较特殊:思路是原生自动化思路去做的,无法切换到webview---原因很多,就是垃圾)

37:面试:(手机自动化相关岗位)

面试点:
  1appium基本原理,手机生态(几大生态:ios(苹果),安卓生态很多)
  2:元素定位方法-xpath(常用三种,什么场景什么方法,平时怎么去用的,复杂的用xpath语法,举例使用xpath语法描述出来) 
  3自动化什么语言编写的,问一些 编程语言 的一些基础知识,基础代码方法,代码功底,封装层级(多练习代码,代码封装,可维护性)
  4:常用的测试框架-pytest(熟悉pytest,掌握基础的pytest基本使用要求) ,rf,unitest
  5:实际项目经验(手机app的界面自动化,,多终端(亮点))(原生应用和混合应用)(aview) 替换成自己的项目,
    做什么了,干什么功能,做的事情按思路写下来, 面试时候写:实现多终端测试和混合应用的测试---这些技术亮点---灵活
多终端:
  架构是一台手机连接一个 appium server,一台连接不上不会影响其他的的运行
  webdriver 驱动只能 windows运行,手机运行不了
  测试用例串起来怎么执行
webview用到的工具: 
  chromedriver--驱动自己下载
  chrome 手机浏览器安装包 
  wv_test.apk 安装包 两个安装包, 

微信小程序(看录播视频)交互比较多
微信公众只是文章的展示,没有自动化的必要, 微信小程序(重点查看)
微信公众号查看(松勤在线课堂也可以查看)(网盘下载的视频)

移动端弹出框和web不一样,其实就是一个应用(弹出框直接以 原生应用定位元素操作元素的方式自动化)
手机自动化相关的工作:准备哪些 
  (浏览器不是混合应用,混合应用是cs架构的,数据需要从服务器上去访问,而浏览器是bs架构的--- 
  浏览器硬要算也属于 混合应用,和普通混合应用有区别,特殊一些,大部分区域都是展示webview里面的内容,
  浏览器操作浏览器的时候不需要切到webview来操作。进去默认就是切到 webview 里面来的) 比较方便,
  严格来说浏览器也算混合应用--但是有些不同, 常识上来说-浏览器是和混合应用分开的
  (因为定义混合应用:就是 原生应用的壳去嵌套的,属于自己开发的,和浏览器有区别)
  浏览器解释网页内容是浏览器自带的,浏览器自带解释器,不需要依靠手机 webview,
  手机 混合应用不会带浏览器解释器,需要靠手机操作系统的webview来渲染显示网页内容) 
  (混合应用(原生应用的壳去嵌套的)cs结构,需要的数据服务器访问)

38:微信小程序发展史

一开始是公众号发展出来的技术分支,微信公众号最早出来的(微信公众号本质上是个网页,网页里面除了有html,css,还有js,通过控制js可以完成一些操作:如:
  存储图片,调用一些计算机相关硬件资源-早期不太规范,规范后微信推出js sdk
  sdk是开发包,给第三方开发者利用开发包提供的接口调用微信自己提供的能力--如:拍摄,语言识别,二维码,地图等功能,,,,等等等等,
  让所有开发者利用微信原生能力,完成一些任务,后来进一步开发就有了小程序的概念   小程序推广:让应用开发没那么难,前端人员会html css,就行,不需要安卓开发ios开发能力才能开发客户端 小程序本质上也是客户端(运行环境嵌套在webview里面的应用,由html,js,css技术构成)小程序一个网页,   只不过这个网页运行环境特殊,只能运行在微信里
---小程序运行环境 安卓环境:逻辑层v8(js执行引擎v8).(v8谷歌浏览器的js执行引擎)   渲染层:chromium定制内核 (逻辑层:想象成js, 渲染层:html/css(chrome定制内核))   安卓的平台是---v8+chrome定制内核 ios:逻辑层:javacriptcore   渲染层:WKWebView (ios有自己的浏览器内核,自己有解析浏览器的东西)   ios的平台是javascriptcore+WKWebView ios有自己的浏览器内核,有自己的一套解析浏览器的东西 小程序还可以运行在小程序开发者工具里面:   逻辑层:NWJS   渲染层:Chrome webview (小程序可以运行在小程序开发者工具里面,逻辑类似 渲染层用的Chrome webview,html css运行环境和安卓采用的一样 小程序:本质是网页,运行一定要在 微信里面(依赖微信提供的原生环境,不能使用浏览器打开)   所以自动化微信小程序不能通过自动化手机页面一样自动化小程序(因为页面打不开)
一:小程序自动化方案:
  1:方案一:(小程序官方提供了自动化的测试库:测试库js编写的)需要要会js语言才能使用这种方案
  2:方案二:小程序本质webview,是运行在webview上面的网页,采用切入微信小程序的webview,然后像操作网页一样操作小程序,看似可行--
    (自动化webview需要chromedriver,chromedriver有bug导致无法切入到微信小程序的wedview里面去)
    无法执行方案二::driver.switch_to.context("WEBVIEW_com.example.haiwen.myhybirdapp")  切换到微信的webview控制
  3:方案三:安卓系统更新后,老的安卓系统不行,估计要安卓6以上的系统才可行,
    可以用原生控件的形式展示webview的内部元素(系统够新,定位工具够强,可以把手机web的html结构以原生控件展示出来)
    然后这时候可以采用自动化原生控件的方式来自动化微信小程序(定位控制原生小程序的方式)
目前只有方案三可行:替补方案。
  微信小程序技术并不是特别新的知识点,都是老的技术,定位控制原生控件的方式,然后控制微信小程序---老方法
二:案例:自动化星巴克实战:
  1:打开微信搜索  星巴克用心说
  2:进入第一个结果,进入小程序
  3:进入请TA喝咖啡
  4:选择点击一杯美式咖啡
  5:点击购买
  6:进入付款页面---测试结束
三:需要找一个元素定位工具可以定位微信小程序里面的元素,
  sdk自带的元素定位工具:  不行,只能定位最外面的一层
  appium自带的工具也可以:(现在定位的元素类型都不是web界面元素,元素类型都是android.view.View)
  元素定位工具:weditor  尝试定位微信小程序里面的元素(sdk自带的定位工具不行)
四:界面元素类型有:android.webkit.WebView(代表我们小程序是一个网页,网页它是运行在WebView里面的)

39:xpath元素定位思路

1:起点元素和终点元素他们中间是什么关系,起点元素父亲的父亲的兄弟是终点元素--爷爷的弟弟,
  //*[@text="美式咖啡"]/../../folloving-sibling::android.view.view    #爷爷只有一个弟弟,不需要写下标了
2:根据//*[@text="拿铁"]进行间接定位,因为元素没啥特征,
  起点元素的爷爷的兄弟的二儿子
  //*[@text="美式咖啡"]/../../folloving-sibling::android.view.view/android.view.View/android.widget.EditText
  xpath元素定位就是梳理元素关系的,麻烦得要死,
  //*[@resource-id="app"]//android.view.View[@text="星巴克用星说"]

  起点元素和终点元素:       
---- 1//*@text="美式咖啡"       ----     ------   ------ 3   根据1元素定位3元素---1和3是爷爷的同级别元素,---爷爷的弟弟     //*@text="美式咖啡"/../../folloving-sibling::android.view.view
      /..父级别
      folloving-sibling::android.view.view   弟弟::元素属性(只有一个不用写下标)   爷爷的兄弟的儿子     //*@text="美式咖啡"/../../folloving-sibling::android.view.view /android.view.view/*/*                    爷爷的弟弟        爷爷弟弟的儿子 爷爷弟弟的孙子 爷爷弟弟孙子的孙子   xpath属性的包含模式:     //*[contains(@text,"星巴克中国") 这个属性的文本包含“星巴克中国” ---@后面是属性,属性包含的值 (第一个参数是属性,第二个参数是属性包含的值)
  ele.size (获取元素的尺寸,高度和宽度)

40:微信小程序总结:了解微信小程序的特征和运行原理

1:微信小程序是微信生态下的混合app,且只能运行在微信环境中(浏览器打开页面的方法无法打开小程序里面的内容,运行环境有要求,只能运行再微信环境里面)
2:小程序自动化其实用的是 appium 控制原生控件的方式(替补方案)
3:元素定位多用 xpath--(原生控件方式去定位)
4:页面切换时候,需要触发隐式等待或者编码硬等待来等待目标出现
(尤其是滑动和坐标点击的时候,不会涉及到寻找元素,不和元素交互,不会触发隐式等待
通过定位目标界面的特定元素来保证我们已经进入到目标界面了,或者直接写死sleep硬等)

41:代码的封装:

1:po模式封装,特定:复用性很强,但是非常繁琐,每个页面需要写对应的方法和对应元素
2:过程,根据过程行为进行封装:复用性不是很高的  ----封装简单
封装步骤:
  1:打开小程序一步 open_miniprogram()   
  2:挑选商品+提交订单等操作   

42:辛巴克实战初级代码

        from appium import webdriver
        import time
        weixin_caps={
            'platformName':'Android',
            'plathformVersion':'10',
            'deviceName':'testxiaomi',
            #目标应用,从微信打开小程序
            'appPackage':'com.tencent.mm',
            'appActivity':'.ui.LauncherUI',
            'noReset':True,
            'newCommandTimeout':6000,
            'automationName':'UiAutomator1',
            'unicodeKeyboard':True,
            'resetKeyboard':True
        }

        driver=webdriver.Remote('http://localhost:4723/wd/hub',weixin_caps)
        driver.implicitly_wait(15)                                 #切换界面有点慢,写长点

                                                                    #下拉之前先等待目标页面出现,可以通过下面这个方法,
                                                                   #因为我们目标元素//android.widget.TextView[@text="微信"]在目标界面里面,
                                                                    #如果目标元素没有出现会触发隐式等待,我们这里设置了15s
        driver.find_element_by_xpath('//android.widget.TextView[@text="微信"]')
        

                                                                    #1:下拉滑动动作,拉出辛巴克
                                                                        (确定起点和终点,向下拉,起点取屏幕1/4的位置,终点取屏幕3/4的位置)
                                                                       #当我们和界面存在交互的时候,一定要考虑交互的界面是不是当前界面,
                                                                        下拉和当前屏幕做一个交互,在我们这个页面一定需要打开微信之后才进行
                                                                         交互,不然微信启动过程中下拉没效果,微信打开的时候需要时间,
                                                                          所有打开微信时候需要等待一下
                                                                          好的方式是寻找界面特定的元素,等目标出现了就认为进入到目标页面了
        size=driver.get_window_size()
        pos_x=size['width']/2                                                                  #x取宽的二分之一
        pos_y=size['height']/4                                                                 #取四分之一
                                                                                            #垂直滑动,终点的x坐标不变,y等于加滑动距离
        distance=size['height']/2                                                              #滑动1/2屏幕距离
        driver.swipe(pos_x,pos_y,pos_x,pos_y+distance)                                        #滑动动作,向下滑动

                                                                                            #2滑动之后,可以偷个懒点击图标第一个,或者点击搜索框,
                                                                                                为了自动化程序强壮,还是搜索小程序靠谱
        driver.find_element_by_xpath('//android.widget.EditText[@text="搜索小程序"]').click()
        
        driver.find_element_by_id('com.tencent.mm:id/bhn').send_keys('星巴克用星说')        #输入关键字星巴克进行搜索
        
        driver.find_element_by_xpath('//*[@resource-id="app"]//android.view.View[@text="星巴克用星说"]').click()                #3:点击星巴克用心说
        
        #weditor元素定位很强大,比其他两个强大,只是使用这个工具后执行代码需要手动关闭uiautmitor,不然代码运行异常
        #元素定位工具给你提供默认的元素定位方法,如果发现元素定位表达式它引用了太多下标,使用一定要谨提,因为页面里面元素发生变化的话,下标可能就会失效
        #手动写xpath元素定位表达式比较保险

                                                                                            #现在已经进入星巴克界面了
        
        driver.find_element_by_xpath('//*[@text="咖啡券"]').click()                            #1:点击请他喝咖啡(咖啡卷)--这里元素定位就简单了

                                                                                            #点击后需要先校验一下有没有进入目标界面,找一个这个界面特有的元素,
                                                                                                --通过购买礼物判断有没有进入目标页面
        #等待进入购买商品页面
        driver.find_element_by_xpath('//*[@text="购买礼物"]')                 #寻找这个元素就可以了,这个元素什么都不用操作,找到这个元素证明已经进入到了目标界面,
                                                                                然后再滑动

                                                                             #2 :这时候需要滑动选择目标元素:拿铁---滑动寻找拿铁咖啡
                                                                             #滑动需要从下到上,定义起点和终点,还用原来的,不过起点和终点位置调过来
        # driver.swipe(pos_x,pos_y+distance,pos_x,pos_y)                      #y调换一下,起点变成终点,终点变成起点--这样操作
                                                                              #需要滑动几次屏幕找到目标元素,可以一边滑动,
                                                                               一边寻找目标元素,滑动的距离distance写的稍微小点,怕错过了元素
        #和boss直聘操作一样
        driver.implicitly_wait(0.5)                                            #进来很久才滑动,因为触发隐式等待15s了,一直找元素15s,所以修改隐式等待时长0.5
        distance2=size['height']/8
        while 1:
                                                                              # 寻找拿铁咖啡,确保找不到不报错用s复数形式,一开始肯定找不到,需要滑动才能找到
            eles=driver.find_elements_by_xpath('//*[@text="拿铁"]')
            if eles:  #如果列表元素不为空,证明已经找到了目标元素,找到目标元素需要添加到购物车里面,点击+号
                print('找到目标商品')
                #点击+号添加商品
                driver.find_element_by_xpath('//*[@text="拿铁"]/../../folloving-sibling::android.view.view ').click()
                #验证是不是一杯
                break
            #最好每次只滑动一格两格的距离,否者有惯性影响
            #获取元素尺寸:ele.size  (获取元素的尺寸,高度和宽度),这里滑动屏幕的1/8
            driver.swipe(pos_x, pos_y + distance2, pos_x, pos_y)

        driver.implicitly_wait(15) #隐式等待时间改回来,后面还有寻找元素的动作,目标元素有的需要加载,不是马上就能显示的
        # 检查商品对应购物栏数字是不是1
        goods_number=driver.find_element_by_xpath(
            '//*[@text="美式咖啡"]/../../folloving-sibling::android.view.view/android.view.View/android.widget.EditText').text
        print(f'当前商品数量{goods_number}')

        # 点击购买礼物,购买礼物没有选择商品之前是灰色的,如果上面的数字是1证明已经添加了商品,购物车由灰色变绿了,可以不用写等待时间,
        # 就可以点击购买礼物按键了
        driver.find_element_by_xpath('//*[@text="购买礼物"]').click()  #

        #最后一步:检查选择支付方式
        driver.find_element_by_xpath('//android.widget.TextView[@text="支付方式"]')  #查看页面是不是到了支付方式界面了
        #支付金额打印出来,实际价格,价格值会变,但是¥这个符号不会变--¥67.00,¥32.00
        #所以使用xpath属性的包含模式定位元素(cotain)
        res=driver.find_element_by_xpath('//*[contains(@text,"¥")]').text
        print(res)  #可以打印文本,也可以校验,逻辑校验,一杯二杯咖啡有没有打折和价格计算,最后检查提交的时候多少钱--计算验证

        driver.quit()    

43:辛巴克实战我的11-2号最新修改的代码:(向下滑动后直接点击星巴克,不是输入查找的)

        from appium import webdriver
        import time
        weixin_caps={
            'platformName':'Android',
            'plathformVersion':'10',
            'deviceName':'testxiaomi',
            #目标应用,从微信打开小程序
            'appPackage':'com.tencent.mm',
            'appActivity':'.ui.LauncherUI',
            'noReset':True,
            'newCommandTimeout':6000,
            'automationName':'UiAutomator1',
            'unicodeKeyboard':True,
            'resetKeyboard':True
        }

        driver=webdriver.Remote('http://localhost:4723/wd/hub',weixin_caps)
        driver.implicitly_wait(15)

        driver.find_element_by_xpath('//android.widget.TextView[@text="微信"]')

        size=driver.get_window_size()
        pos_x=size['width']/2
        pos_y=size['height']/4
        distance=size['height']/2
        driver.swipe(pos_x,pos_y,pos_x,pos_y+distance)

        driver.find_element_by_id('com.tencent.mm:id/cna').click()  #下拉后点击星巴克小程序
        time.sleep(6)

        driver.find_element_by_xpath('//*[@text="一份心意,简单得体"]').click()
        driver.find_element_by_xpath('//*[@text="购买礼物"]')
        driver.implicitly_wait(0.5)
        distance2=size['height']/8
        while 1:
            eles=driver.find_elements_by_xpath('//*[@text="拿铁"]')
            if eles:
                print('找到目标商品')
                driver.find_element_by_xpath('//*[@text="拿铁"]/../../folloving-sibling::android.view.view ').click()
                break
            driver.swipe(pos_x, pos_y + distance2, pos_x, pos_y)

        driver.implicitly_wait(15)
        goods_number=driver.find_element_by_xpath(
            '//*[@text="美式咖啡"]/../../folloving-sibling::android.view.view/android.view.View/android.widget.EditText').text
        print(f'当前商品数量{goods_number}')

        driver.find_element_by_xpath('//*[@text="购买礼物"]').click()  #

        driver.find_element_by_xpath('//android.widget.TextView[@text="支付方式"]')

        res=driver.find_element_by_xpath('//*[contains(@text,"¥")]').text
        print(res)
        driver.quit()    

44:小程序星巴克实战函数封装版本:结合pytest框架

        import os
        import time

        import pytest
        from appium import webdriver
        from day6.start_bark import weixin_caps
        from selenium.webdriver.support.wait import WebDriverWait
        from selenium.webdriver.support import expected_conditions as EC


        def open_session(port,caps):
            global driver
            driver=webdriver.Remote(f'http://localhost:{port}/wd/hub',caps)
            driver.implicitly_wait(15)

        def close_session():
            global driver
            driver.quit()

        #滑动距离为负数-向上滑动,反之向下滑动,起点为屏幕中心点
        def swipe_screen(distance):
            size = driver.get_window_size()
            pos_x = size['width'] / 2
            pos_y = size['height'] / 2
            driver.swipe(pos_x, pos_y, pos_x, pos_y + distance)

        #打开小程序方法
        def open_miniprogram(name):
            global driver
            # 先等待目标页面出现
            driver.find_element_by_xpath('//*[@text="微信"]')
            # 下拉
            size = driver.get_window_size()
            distance = size['height'] / 2-10
            #向下距离为正数
            swipe_screen(distance)
            time.sleep(1)
            # 点击搜索框
            driver.find_element_by_id('com.tencent.mm:id/lj').click()
            # 输入关键字星巴克用星说
            driver.find_element_by_id('com.tencent.mm:id/bhn').send_keys(name)
            # 点击搜索结果
            driver.find_element_by_xpath('//*[@resource-id="app"]//android.view.View[@text="星巴克用星说"]').click()
            time.sleep(1.5)
            # 点击回车键
            driver.keyevent(66)
            # 选择第一个结果
            res=driver.find_elements_by_xpath(
                '//*[@resource-id="app"]/android.view.View[2]/android.view.View[2]/android.view.View[1]/android.widget.Button[1]')
            if res:
                res[0].click()


        #挑选商品+提交订单到付款界面
        def select_items(item):
            # 请他喝咖啡
            driver.find_element_by_xpath('//*[@text="请TA喝咖啡"]').click()

            # 等待进入购买商品页面
            driver.find_element_by_xpath('//*[@text="购买礼物"]')
            # 滑动寻找美式咖啡
            driver.implicitly_wait(0.5)
            size = driver.get_window_size()
            distance2 = -(size['height']/8)
            while 1:
                # 寻找美式咖啡
                eles = driver.find_elements_by_xpath(f'//*[@text="{item}"]/../../following-sibling::android.view.View')
                if eles:
                    swipe_screen(-200)
                    time.sleep(1)
                    print('找到目标商品')
                    # 点击加号
                    eles[0].click()
                    break
                swipe_screen(distance2)
            driver.implicitly_wait(15)
            
            # 检查商品对应购物栏数字
            goods_num = driver.find_element_by_xpath(
                f'//*[@text="{item}"]/../../following-sibling::android.view.View/android.view.View/*/*').text

            print('当前商品数量: %s' % goods_num)

            # 点击购买礼物
            driver.find_element_by_xpath('//*[@text="购买礼物"]').click()

            # 检查进入选择支付方式
            driver.find_element_by_xpath('//*[@text="选择支付方式"]')

            # 支付金额查看
            res = driver.find_element_by_xpath('//*[contains(@text,"星巴克中国")]').text
            print(res)

        #给初始化加上启动参数
        @pytest.fixture(params=[('4723',android8),('4725',android9)])
        def before_test(request):
            port=request.param[0]
            cpas=request.param[1]
            open_session(port,cpas)
            yield
            after_test()

        def after_test():
            close_session()


        def test_miniprogram(before_test):
            open_miniprogram('星巴克用星说')
            select_items('美式咖啡')


        if __name__ == '__main__':
            #提前装好pytest-xdist插件: pip install pytest-xdist
            #-n 2表示用两个进程启动测试脚本,因为该测试有两组参数
            pytest.main(['start_bark.py','-s','-n 2','--alluredir=tmp/my_allure_results'])
            # os.system(f'allure serve tmp/my_allure_results')    

45:小程序星巴克实战多终端版本:结合pytest框架

        import os
        import time

        import pytest
        from appium import webdriver
        from mini_config import android8,android9

        def open_session(port,caps):
            global driver
            driver=webdriver.Remote(f'http://localhost:{port}/wd/hub',caps)
            driver.implicitly_wait(15)

        def close_session():
            global driver
            driver.quit()

        #滑动距离为负数-向上滑动,反之向下滑动,起点为屏幕中心点
        def swipe_screen(distance):
            size = driver.get_window_size()
            pos_x = size['width'] / 2
            pos_y = size['height'] / 2
            driver.swipe(pos_x, pos_y, pos_x, pos_y + distance)

        #打开小程序
        def open_miniprogram(name):
            global driver
            # 先等待目标页面出现
            driver.find_element_by_xpath('//*[@text="微信"]')
            # 下拉
            size = driver.get_window_size()
            distance = size['height'] / 2-10
            #向下距离为正数
            swipe_screen(distance)
            time.sleep(1)
            # 点击搜索框
            driver.find_element_by_id('com.tencent.mm:id/lj').click()
            # 输入关键字星巴克用星说
            driver.find_element_by_id('com.tencent.mm:id/bhn').send_keys(name)
            # 点击搜索结果
            driver.find_element_by_xpath('//*[@resource-id="app"]//android.view.View[@text="星巴克用星说"]').click()
            time.sleep(1.5)
            # 点击回测键
            driver.keyevent(66)
            # 选择第一个结果
            res=driver.find_elements_by_xpath(
                '//*[@resource-id="app"]/android.view.View[2]/android.view.View[2]/android.view.View[1]/android.widget.Button[1]')
            if res:
                res[0].click()


        #挑选商品+提交订单
        def select_items(item):
            # 请他喝咖啡
            driver.find_element_by_xpath('//*[@text="请TA喝咖啡"]').click()

            # 等待进入购买商品页面
            driver.find_element_by_xpath('//*[@text="购买礼物"]')
            # 滑动寻找美式咖啡
            driver.implicitly_wait(0.5)
            size = driver.get_window_size()
            distance2 = -500
            while 1:
                # 寻找美式咖啡
                eles = driver.find_elements_by_xpath(f'//*[@text="{item}"]')
                if eles:
                    print('找到目标商品')
                    # 点击加号
                    driver.find_element_by_xpath(f'//*[@text="{item}"]/../../following-sibling::android.view.View').click()
                    break
                swipe_screen(distance2)
            driver.implicitly_wait(15)
            # 检查商品对应购物栏数字
            goods_num = driver.find_element_by_xpath(
                f'//*[@text="{item}"]/../../following-sibling::android.view.View/android.view.View/*/*').text

            print('当前商品数量: %s' % goods_num)

            # 点击购买礼物
            driver.find_element_by_xpath('//*[@text="购买礼物"]').click()

            # 检查进入选择支付方式
            driver.find_element_by_xpath('//*[@text="选择支付方式"]')

            # 支付金额查看
            res = driver.find_element_by_xpath('//*[contains(@text,"星巴克中国")]').text
            print(res)

        #给初始化加上启动参数
        @pytest.fixture(params=[('4723',android8),('4725',android9)])
        def before_test(request):
            port=request.param[0]
            cpas=request.param[1]
            open_session(port,cpas)
            yield
            after_test()

        def after_test():
            close_session()


        def test_miniprogram(before_test):
            open_miniprogram('星巴克用星说')
            select_items('美式咖啡')


        if __name__ == '__main__':
            #提前装好pytest-xdist插件: pip install pytest-xdist
            #-n 2表示用两个进程启动测试脚本,因为该测试有两组参数
            pytest.main(['start_bark.py','-s','-n 2','--alluredir=tmp/my_allure_results'])
            # os.system(f'allure serve tmp/my_allure_results')