更新:
下载路径:https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html
设置变量:
验证变量:java -version
下载路径:https://nodejs.org/zh-cn/
验证环境:node -v
下载地址:https://www.androiddevtools.cn/
解压后打开SDK
更新-需要梯子
或者下载包后直接覆盖
更新成功后
设置环境变量
版本下载路径:https://github.com/appium/appium-desktop/releases
安装完成后设置环境变量
设置开发者模式:
将D:\Software\Android\android-sdk-windows\platform-tools adb.exe复制到 D:\Software\Android\Nox\bin 下 并重命名为nox_adb.exe
{
"platformName": "Android",
"platformVersion": "7.1.2",
"deviceName": "kao",
"appPackage": "tv.danmaku.bili",
"appActivity": "tv.danmaku.bili.MainActivityV2",
"autoAcceptAlerts": "true"
}
下载路径:https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html
下载路径:https://nodejs.org/zh-cn/
node.js安装很简单,一路点击下一步就可完成安装,安装完成在cmd输入node -v,出现下图就说明安装成功。
2.3 安装SDK
下载路径:https://www.androiddevtools.cn/
点击SDK Tools,出现下图,建议选择installer_r24.4.1-windows.exe
下载完毕,开始安装,那个默认勾选即可,后续也可以在安装路径下找到SDK Manager 打开
C:\Users\Administrator\AppData\Local\Android\android-sdk
以下图中四项勾选是必须的,勾选后点击右下角install 17 packages。
进入安装包选择,这个Android SDK License是必须的,选中这个点击右下角Accept License,然后install,耐心等待安装即可。
环境变量配置,系统变量增加一个变量 ANDROID_HOME ,而path变量增加图片二中的两个路径。
cmd中输入adb vesion验证,出现下图所示说明安装成功。
下载路径:http://appium.io/
历史版本下载路径:https://github.com/appium/appium-desktop/releases
1、建议下载Appium-windows-1.21.0.exe,点击即可下载。
2、选择合适安装路径,一直确定等待安装完成即可。
3、环境变量配置,如下图。
模拟器配置:以夜神模拟器为例
1、我的夜神模拟器安装路径为C:\Program Files (x86)\Nox,设置新的系统变量如下:
2、前提是已安装SDK,打开cmd,输入adb devices,发现并连接设备。
3、cmd中进入到夜神模拟器中nox_adb.exe的目录,nox_adb.exe connect 127.0.0.1:62001,之后关闭模拟器输入adb devices如第二个图片所示说明连接成功。
补充
把android-sdk里面的adb.exe版本复制出来,然后改个名称叫nox_adb.exe,替换nox安装的路径:\Nox\bin下的nox_adb.exe文件就行了
4、前提已安装appium,打开appium,依次点击下列图片中红色框。
5、在下面图示内容中红色框输入要打开的app信息,选择已下代码参数。
‘platformName’: ‘Android’, # 输入平台,这里是安卓
‘plathformVersion’: ‘7’, # 输入系统版本,这里是安卓7.1.2
‘deviceName’: ‘127.0.0.1:62001 device’, # 设备的识别名字,adb devices获取
‘appPackage’: ‘com.android.settings’, # app包名称
‘appActivity’: ‘.Settings’, # 直接打开app的某个活动,相当于打开web中某个页面
‘autoAcceptAlerts’:‘true’ # 连接实机时,一般会有授权提示,这里选择true就是默认授权
6、配好相关参数,点击start session就可以了,这旁边的Save As是用来保存参数配置的。
注:过程中可能遇到的问题。
问题:连接时会报错 “adb server version doesn’t match this client ”。
原因:SDK服务端的adb版本与客户端(夜神)的adb 版本不匹配。
解决:确保adb和nox_adb不在运行中,将platform-tools的adb复制并且重命名为“nox_adb”然后粘贴到夜神的bin文件夹下将其之前的覆盖就好了。
实机操作:以小米Android7为例
1、手机和电脑在同一个局域网内 ,可通过相同wifi解决或者手机设置代理,代理ip和端口就是电脑ip和端口
2、打开手机开发者模式,可通过连续点击手机系统版本号开启或者设置-更多-开发者选项开启
3、数据线连接电脑,cmd中adb devices可用查看到已连接的设备号
4、cmd中使用adb shell 进入shell中,再dumpsys activity |grep mFocusedActivity找到app包名和activity程序名
注:adb命令使用
1.Native app
Native App是一种基于智能手机本地操作系统如iOS、Android、WP并使用原生程式编写运行的第三方应用程序,也叫本地app。一般使用的开发语言为JAVA、C++、Objective-C。
2.Hybrid app
Hybrid App(混合模式移动应用)是指介于web-app、native-app这两者之间的app,兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。
3.web-app
Android调试桥(adb)是一个通用命令行工具,其允许你与模拟器实例或连接的Android设备进行通讯。他可为各种设备操作提供便利,如安装和调试应用,并对Unix shell(可用来在模拟器或连接的设备上运行各种命令)的访问。
1、adb help
查看帮助手册
2、adb devices
检测连接到电脑的安卓设备
3、adb pull<手机路径><本机电脑路径>
从手机拉取信息到本地电脑
4、adb push<本机电脑路径><手机路径>
从本机推送信息到手机上去
5、adb shell
登录设备shell模式(命令行的人机界面)
1、adb install xxx.apk
安装命令
2、adb uninstall com.tencent.mobileqq
卸载命令
3、adb shell dumpsys activity | find “mFocusedActivity”
查看前台正在运用的包名
4、adb kill-server
终止adb服务
5、adb start-server
启动adb服务
1、aapt dump badging
获取APP的包名与入口
例:
包名 package: name=‘com.tencent.mm’
入口 launchable-activity: name=‘com.tencent.mm.ui.LauncherUI’ label=‘WeChat’
2、adb shell an start -n 包名/入口
启动APP
3、adb shell pm 包名
清除应用的数据和缓存
4、adb shell input tap x坐标 y坐标
坐标点击
获取坐标的方式:打开开发者选项下的指针位置功能
5、adb shell pm list packages
列出所有包名
-s 列出系统apk路径及包名
-3 列出用户apk路径及包名
6、adb logcat
打印日志
通过python代码操作不同操作系统(ios、android)不同版本手机终端
Android sdk环境(UiAutomator1,UiAutomator2):不同系统不同版本差异比较大。不同的系统不同的版本用到不同的软件包/框架,所以自动化脚本需要指定操作终端的设置参数
官方文档链接:https://appium.io/docs/en/writing-running-appium/caps/#general-capbilities
1、automationName 当前用的引擎、框架 Appium (default)
2、newCommandTimeout 默认60秒 60秒没有发送其他请求,会关闭应用程序并回到首页
3、noReset 不重置,保留用户原有的信息在app上
4、appActivity 入口启动页面,如下图
5、appPackage 包名
Adb connect 127.0.0.1:62001 这个是夜神模拟器的
稳定性和兼容性才用真机测试
查看连接的设备
Adb devices
需要进入aapt所在的路径进行输入命令 build-tools目录下
Aapt dump badging 本地文件路径与app名称
包名:com.tal.kaoyan
Aapt dump badging 本地文件路径与app名称 | findstr package
com.tal.kaoyan.ui.activity.SplashActivity
蓝色图标:android sdb环境下调用工具包与框架实现的一些操作
黄色图标:警告信息,可以不看
灰色图标:当前客户端,自动化脚本执行的操作
1、自动化脚本发送http请求,请求参数:终端设置参数
2、创建会话
3、确认终端设备是否连接,并确认安卓脚本,确认设置参数是否与终端设置参数一致
Appium会推送一个包AppiumBootstrap.jar 模拟器上 api包:appium server指令进行接收,操控手机端
4、响应http请求
5、下一个http请求,重复以上操作
建议使用weditor进行定位
Android SDK自带的工具
位置android sdk/tools/ uiautomatorviewer.bat
Text:
Resoureid:
Class:元素标签
Content-desc:元素功能描述 语音播报
注意:在截屏的时候,必须当前截屏的终端设备没有其他的进程在占用,包括appium server
AIUautomator2 python的第三方库 通过python脚本实现对appUI自动化测试
安装:
命令1:pip install Uiautomator2
命令2:python -m uiautomator2 init 推送一个包在模拟器上面
命令3:pip install weditor 需要科学上网进行安装
确认安装:weditor –help
绕过科学上网方式:
pip install keras -i http://pypi.douban.com/simple --trusted-host pypi.douban.com(其中的keras是你需要下载的,根据自己需求自行更改)
阿里云 http://mirrors.aliyun.com/pypi/simple/
中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban) http://pypi.douban.com/simple/
清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学 http://pypi.mirrors.ustc.edu.cn/simple/
id、ClassName、accessbility、xpath
通过resourceid属性定位 find_element_by_id 返回WebElement对象
2、通过文本定位 find_element_by_android_uiautomator()
driver.find_element_by_android_uiautomator(‘new UiSelector().text(“手机号”)’).send_keys(“18781418682”)
调用系统自带的框架(UIautomator1/ UIautomator12)实现元素的定位,基于java代码编写 UiSelector实现定位,提供了很多方法,通过多个属性实现元素定位
driver.find_element_by_android_uiautomator(‘new UiSelector().text(“登 录”).resourceId(“com.taihe.fans:id/btn_login”)’).click()
driver.find_element_by_accessibility_id(“密码 安全”).send_keys(“saff”)
driver.find_element_by_xpath(‘//*[@resource-id=“com.tencent.mobileqq:id/login”]’).click()
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
#字典
app_parameter={
"platformName":"Android",
"platformVersion":"7.1.2",
"deviceName":"kao",
"appPackage":"tv.danmaku.bili",
"appActivity":"tv.danmaku.bili.MainActivityV2",
"noReset":True
}
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",app_parameter)
#id定位
driver.find_element(AppiumBy.ID('com.jiyong.rta.debug:id/edt_customer_name'))
#className定位
driver.find_element(AppiumBy.CLASS_NAME('android.widget.EditText'))
#content-desc 定位
#这个是用来描述当前元素的功能的,类似于备注。但绝大多数情况下,除非硬性规定,这个属性值一般为空,所以实用性不高。
driver.find_element(AppiumBy.ACCESSIBILITY_ID('这是一个按钮'))
#Android Uiautomator定位
#txt定位
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,'new UiSelector().text("我的")').click()
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,'new UiSelector().text("会员购")').click()
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,'new UiSelector().text("首页")').click()
#txt定位模糊定位#
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textContains("会员")').click()
#txt定位(正则匹配式)
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().textMatches("^会员.*")').click()
#resourceId定位
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.jiyong.rta.debug:id/edt_customer_name")')
#className定位
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.EditText")')
#className定位(正则匹配式)
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().classNameMatches(".*EditText")')
# id与text组合定位
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.jiyong.rta.debug:id/edt_customer_name").text("顾客名称")')
# className与text组合定位
driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.EditText").text("顾客名称")')
点击:click()
输入文本:send_keys()
获取文本:text()
获取属性的值:get_attribute() 例:.get_attribute(“description)
def swipe(self: T, start_x: int, start_y: int, end_x: int, end_y: int, duration: int = 0) -> T:
参数说明
Args:
start_x: 开始位置的X坐标
start_y: 开始位置的Y坐标
end_x: 结束位置的X坐标
end_y: 结束位置的Y坐标
duration: 延时多少毫秒
左滑:开始位置的X坐标>结束位置的X坐标 开始位置的Y坐标=结束位置的Y坐标
#获取屏幕尺寸
windows_size = driver.get_window_size()
x=windows_size["widht"]
y=windows_size["height"]
#左滑两次
for i in range(2):
driver.swipe(start_x=x*0.9,
start_y=0.5,
end_x=x*0.3,
end_y=y*0.5,
duration=1000)
POM page object model 页面对象模型,主要用于UI自动化测试框架搭建,主流设计模式之一
页面对象模型:结合面向对象编程思路,把项目的每个页面当作一个对象来进行编程
Python基础:什么对象?python怎么描述一个对象=属性+行为
通过类定义=具有相同属性+相同行为对象集合
POM一般分为四层:项目=n个页面=base层+pageobject层(页面1,页面2,页面3,….页面n)
第一层:base层 描述每个页面相同属性及行为
第二层:pageobject层 每个的独有特征及独有行为
第三层:testcases层 用例层 ,描述项目业务流程
第四层:testdata 数据层
Pom模式设计意义:层次更加清晰,方便管理以及提供代码复用性及扩展性
核心技能:搭建自动化框架(代码)
以手机APP考研帮为例
from appium import webdriver
desired_caps={
"platformName":"Android",
"platformVersion":"7.1.2",
"deviceName":"yesheng",
"appPackage":"com.tal.kaoyan",
"appActivity":"com.tal.kaoyan.ui.activity.SplashActivity",
"noReset":True
}
from appium import webdriver
driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
driver.implicitly_wait(30)
#点击同意按钮
el_agree = driver.find_element_by_id("com.tal.kaoyan:id/tip_commit")
el_agree.click()
#点击我知道了
el_iknow = driver.find_element_by_id("com.tal.kaoyan:id/tv_ok")
el_iknow.click()
#点击确定
el_determine = driver.find_element_by_id("android:id/button1")
el_determine.click()
#再次点击确定
el_determine1 = driver.find_element_by_id("android:id/button1")
el_determine1.click()
#点击密码登录
el_PasswordLogin = driver.find_element_by_id("com.tal.kaoyan:id/loginRegistorcodeAndPassword")
el_PasswordLogin.click()
#输入手机号
el_inputphone = driver.find_element_by_id("com.tal.kaoyan:id/loginEmailEdittext")
el_inputphone.send_keys("18788888682")
#输入密码
el_inputpasswd = driver.find_element_by_id("com.tal.kaoyan:id/loginPasswordEdittext")
el_inputpasswd.send_keys("ht1923501505")
#同意用户协议
el_agreement = driver.find_element_by_id("com.tal.kaoyan:id/loginTreatyCheckboxPassword")
el_agreement.click()
#点击登录按钮
el_Signin = driver.find_element_by_id("com.tal.kaoyan:id/loginLoginBtn")
el_Signin.click()
#点击跳过
el_skip = driver.find_element_by_id("com.tal.kaoyan:id/kylogin_perfect_tag_jump_button")
el_skip.click()
#关闭弹窗
elPopup = driver.find_element_by_id("com.tal.kaoyan:id/ivAdViewClose")
elPopup.click()
#定义一个类,描述每个页面相同的属性及行为
#定义一个类,描述每个页面相同的属性及行为
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
class BasePage:
# pass
# #属性 终端参数与发送指令到appium server
# desired_caps = {
# "platformName": "Android",
# "platformVersion": "6.0.1",
# "deviceName": "127.0.0.1:7555",
# "appPackage": "com.taihe.fans",
# "appActivity": "com.taihe.fans.ui.WelcomeActivity",
# "noReset": True
# }
#
# # 2、appium server进行启动
# # 3、 发送指令给appium server
# driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
#
# #实例属性
# def __init__(self,driver):
# self.driver = driver
#
#
# # 行为 输入 点击 元素的定位
# def locator(self,driver):
#用例层再把数据传过来
def __init__(self,driver):
self.driver=driver
#行为?输入与点击
#元素定位
# def locator(self):
# pass
# # loc = MobileBy.ID,"resourceid"
# # return self.driver.find_element(MobileBy.ID,"resourceid值")
#
# loc = MobileBy.ID, "resourceid"
# return self.driver.find_element(*loc) #*分解loc的两个元素
#构造函数
def locator(self,loc):
return self.driver.find_element(*loc) #为什么用return 需要得到driver.find_element数据,用于被下面的方法调用
#输入
def input(self,loc,value):
self.locator(loc).send_keys(value)
#点击
def click(self,loc):
self.locator(loc).click()
#滑动(上下左右滑动)
def swipe(self,start_x,start_y,end_x,end_y,duration=0):
# 获取屏幕尺寸
windows_size = self.driver.get_window_size()
x = windows_size["widht"]
y = windows_size["height"]
self.driver.swipe(start_x=x * start_x,
start_y=y * start_y,
end_x=x * end_x,
end_y=y * end_y,
duration=duration)
#yaml 数据清晰,跨平台,不同平台的数据转化,读取速度快
import yaml
import os
def redYaml(path):
with open(path,"r+",encoding="utf-8") as file:
data = yaml.load(stream=file,Loader=yaml.FullLoader)
return data
if __name__ == "__main__":
rootPath = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) #根据当前文件路径获取day_3的路径
path = os.path.join(rootPath,"config\config.yaml") #路径拼接,获取config.yaml的路径
print(path)
print(redYaml(path))
##yaml 数据清晰,跨平台,不同平台的数据转化,读取速度快,方便维护
desired_caps:
platformName: Android
platformVersion: 7.1.2
deviceName: 127.0.0.1:62001
appPackage: com.tal.kaoyan
appActivity: com.tal.kaoyan.ui.activity.SplashActivity
noReset: False
from day_3.base.basepage import BasePage
from appium.webdriver.common.mobileby import MobileBy
class LoginPage(BasePage):
pass
#登录页面=base层属性及行为+当前页面类定义的属性及行为,需要继承BasePage类
#特有的属性
el_agree = (MobileBy.ID,"com.tal.kaoyan:id/tip_commit")
el_iknow = (MobileBy.ID,"com.tal.kaoyan:id/tv_ok")
el_determine = (MobileBy.ID,"android:id/button1")
el_determine1 = (MobileBy.ID,"android:id/button1")
# el_determine2 = (MobileBy.ID,"android:id/button1")
# el_determine3 = (MobileBy.ID,"android:id/button1")
# el_determine4 = (MobileBy.ID,"android:id/button1")
# el_determine5 = (MobileBy.ID,"android:id/button1")
el_PasswordLogin = (MobileBy.ID,"com.tal.kaoyan:id/loginRegistorcodeAndPassword")
el_inputphone = (MobileBy.ID,"com.tal.kaoyan:id/loginEmailEdittext")
el_inputpasswd = (MobileBy.ID,"com.tal.kaoyan:id/loginPasswordEdittext")
el_agreement = (MobileBy.ID,"com.tal.kaoyan:id/loginTreatyCheckboxPassword")
el_Signin = (MobileBy.ID,"com.tal.kaoyan:id/loginLoginBtn")
el_skip = (MobileBy.ID,"com.tal.kaoyan:id/kylogin_perfect_tag_jump_button")
el_Popup = (MobileBy.ID,"com.tal.kaoyan:id/ivAdViewClose")
#特有的行为
pass
def login(self,username,passwd):
self.click(self.el_agree)
self.click(self.el_iknow)
# for i in range(6):
# self.click(self.el_determine)
self.click(self.el_determine)
self.click(self.el_determine1)
# self.click(self.el_determine2)
# self.click(self.el_determine3)
# self.click(self.el_determine4)
# self.click(self.el_determine5)
self.click(self.el_PasswordLogin)
self.input(self.el_inputphone,username)
self.input(self.el_inputpasswd,passwd)
self.click(self.el_agreement)
self.click(self.el_Signin)
self.click(self.el_skip)
self.click(self.el_Popup)
# #滑动的调用
# self.swipe(0.9,0.5,0.3,0.5,1000)
# self.swipe(0.9, 0.5, 0.3, 0.5, 1000)
from appium import webdriver
from day_3.pageobject.login_page import LoginPage
import pytest
# class TestLogin:
# def test_login(self):
# #启动参数
# desired_caps = {
# "platformName": "Android",
# "platformVersion": "7.1.2",m
# "deviceName": "127.0.0.1:62001",
# "appPackage": "com.tal.kaoyan",
# "appActivity": "com.tal.kaoyan.ui.activity.SplashActivity",
# "noReset": True
# }
# #连接appiumserver
# driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
#
# log_page = LoginPage(driver=driver)
# log_page.login("18788888682","ht1923501505")
# class TestLogin:
# def setup(self):
# desired_caps = {
# "platformName": "Android",
# "platformVersion": "7.1.2",
# "deviceName": "127.0.0.1:62001",
# "appPackage": "com.tal.kaoyan",
# "appActivity": "com.tal.kaoyan.ui.activity.SplashActivity",
# "noReset": True
# }
# # 连接appiumserver
# self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
#
# def test_login(self):
# # #启动参数
# # desired_caps = {
# # "platformName": "Android",
# # "platformVersion": "7.1.2",
# # "deviceName": "127.0.0.1:62001",
# # "appPackage": "com.tal.kaoyan",
# # "appActivity": "com.tal.kaoyan.ui.activity.SplashActivity",
# # "noReset": True
# # }
# # #连接appiumserver
# # driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
#
# log_page = LoginPage(driver=self.driver)
# log_page.login("18888818682","ht1923501505")
from day_3.common.data_util import redYaml
import os
# class TestLogin:
# def setup(self):
# rootPath = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) # 根据当前文件路径获取day_3的路径
# path = os.path.join(rootPath, "config\config.yaml") # 路径拼接,获取config.yaml的路径
# data = redYaml(path)
# print(data)
# self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',data["desired_caps"])
# def test_login(self):
# log_page = LoginPage(driver=self.driver)
# log_page.login("18781418682", "ht1923501505")
from day_3.common.data_util import redYaml
import os
class TestLogin:
def setup(self):
rootPath = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) # 根据当前文件路径获取day_3的路径
path = os.path.join(rootPath, "config\config.yaml") # 路径拼接,获取config.yaml的路径
data = redYaml(path)
print(data)
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub',data["desired_caps"])
@pytest.mark.parametrize("usname,pswd",[("18788888682", "ht8883501505"),("1878141", "h01505"),("187814341", "h0152405")])
def test_login(self,usname,pswd):
log_page = LoginPage(driver=self.driver)
log_page.login(usname,pswd)
if __name__ == "__main__":
pytest.main()