adb和appium的基本知识

摘要:记录了测试中adb的基本命令以及appium的使用步骤。

adb调试工具

adb的概念

  • 开发安卓应用的程序员必须要掌握
  • 测试工程师在做测试时会用到

adb的构成和工作原理

image-20200608113936800

adb的常用命令

  • 获取包名和界面名

    1.包名和界面名的概念

    包名(package):决定文件的唯一性

    界面名(activity):一个界面对应一个一个界面名

    2.获取包名和界面名

    1.打开测试的应用程序

    2.输入adb命令

    命令格式:

    • Mac/Linux:

      1
      adb shell dumpsys window windows | grep mFocusedApp
    • Windows:

      1
      adb shell dumpsys window windows | findstr mFocusedApp

      会显示:

      mFocusedApp=AppWindowToken{29920155 token=Token{26b8340c ActivityRecord{1f97333f u0 com.android.settings/.Settings t2}}}

      包名:com.android..settings

      界面名:.Settings

  • 文件传输

    1.发生文件到手机

    命令格式:

    1
    adb push 电脑的文件路径 手机的文件夹路径

    例如:

    adb push C:\Users\hcy81\Desktop\33.txt /sdcard

    2.从手机拉取文件

    命令格式:

    1
    adb pull 手机的文件夹路径 电脑的文件路径

    例如:

    adb pull /sdcard/33.txt C:\Users\hcy81\Desktop

  • 获取app启动时间

    应用场景:

    1.对启动时间有要求

    2.测试标准:参照同类软件,启动时间不能超过一倍即可

    命令格式:

    1
    adb shell am start -W 包名/启动名

    示例:

    作用:启动com.android.settings 程序进入界面(.Settings)

    adb shell am start -W com.android.settings/.Settings

    image-20200608135029472

    image-20200608135110433

  • 获取手机日志

    应用场景:

    将bug的日志文件发送给开发,便于定位bug

    使用步骤:

    1.打开需要测试的应用

    2.找到触发的bug位置

    3.使用查看日志的命令

    4.触发bug

    5.获取日志信息

    命令格式:

    1
    adb logcat
  • 其他命令

    image-20200608140431938

appium

快速体验(启动参数)

  1. 打开手机模拟器
  2. 打开appium工具
  3. 创建py文件
  4. 将以下代码复制并运行即可
1
2
3
4
5
6
7
8
9
10
11
12
13
from appium import webdriver
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'
driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)

time.sleep(5)
driver.quit()

参数解释:

image-20200608141606092

启动过程:

  1. appium的启动实际.上是在本机使用了4723端口开启了一个服务
  2. 我们写的python代码会访问本机的appium服务器,并获取driver对象
  3. appium 会将我们的driver对象调用的方法转化成post请求,提交给appium服务器
  4. appium 通过接收到的post请求发送给手机,再由手机进行执行

appium基础操作API

  1. 能够使用appium在脚本内启动其他app
  2. 能够使用appium获取包名和界面名
  3. 能够使用appium关闭app和驱动对象
  4. 能够使用appium安装和卸輯app
  5. 能够使用appium将应用置于后台

在脚本内启动其他app

应用场景:

如果一个应用需要跳转到另外-一个应用,就可以使用这个api进行应用的跳转,就像我们通过外卖应用下订单之后会跳转到支付应用一样。

方法名和参数:

1
2
3
4
5
#脚本启动其他app
#参数:
# appPackage:要打开的包名
# appActivity:要打开的界面名
driver.start_activity(appPackage,appActivity)

示例:

打开设置5s后跳转到短信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from appium import webdriver
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
time.sleep(5)

driver.start_activity('com.android.mms','.ui.ConversationList')
time.sleep(5)

driver.quit()

获取app的包名和界面名

应用场景:

当我们从一个应用跳转到另外-个应用的时候,想输出其包名、界面名或者想在报告中展现对应信息,我们就可以调用这个属性来进行获取。

属性名:

1
2
3
4
#获取包名:
driver.current_package
#获取界面名:
driver.current_activity

示例:

打开设置后输出当前包名和应用名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from appium import webdriver
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
time.sleep(5)

print(driver.current_package)
print(driver.current_activity)

driver.quit()

关闭app和驱动对象

方法:

1
2
3
4
#关闭当前的app,不会关闭驱动对象
driver.close_app()
#关闭驱动对象,同时关闭所以关联的app
driver.quit()

示例:

打开设置, 使用close. app()方法关闭,再尝试使用quit()方法,最后打印当前程序的包名,观察区别

close_app:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from appium import webdriver
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
time.sleep(3)

driver.close_app()
print(driver.current_package)

image-20200608145340436

quit:(报错是因为driver对象消失)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from appium import webdriver
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

driver = webdriver.Remote('http://localhost:4723/wd/hub',desired_caps)
time.sleep(3)

driver.quit()
print(driver.current_package)

image-20200608145514813

安装卸载以及确认是否安装app

参数:

1
2
3
4
#安装app
#参数:
# app_path:apk路径
driver.install_app(app_path)
1
2
3
4
#卸载app
#参数:
# app_id:app的包名
driver.remove_app(app_id)
1
2
3
4
5
6
#判断是否安装app
#参数:
# app_id:app的包名
#返回值:
#布尔值:true/false
driver.is_app_installed(app_id)

将应用置于后台

应用场景:

银行类app会在进入后台一定时间后, 如果再回到前台也页面会重新输入密码,如果需要自动化测试这种功能,可以使用这个api进行测试。

参数:

1
2
3
4
#app放置到后台一定时间后再回到前台,模拟热启动(会自动返回前台)
#参数:
#seconds:后台停留时间
driver.background_app(seconds)

注意到:

热启动:表示进入后台回到前台,关机再开这种断电行为叫”冷启动“

UIAutomatorViewer

能够使用UIAutomatorViewer获取元素的特征信息

UIAutomatorViewer使用

应用场景:

定位元素时必须根据元素的相关特征进行定位,而UIAutomatorViewer就是获取元素特征。

简介:

UIAutomatorViewer用来扫描和分析Android的UI控件的工具。

使用步骤:

  1. 进入sdk目录下的目录
    • mac在tools/bin下,打开UIAutomatorViewer
    • Windows在tools/bin下,打开UIAutomatorViewer.bat
  2. 电脑连接真机或者模拟器
  3. 启动待测app
  4. 点击UIAutomatorViewer的左上角Device Screenshot
  5. 点击控件
  6. 查看右下角Node Detail相关信息

示例:

查看《设置》应用程序右,上角“放大镜”按钮的”resource-id”

1.打开uiautomatorviewer

2.打开android模拟器

3.启动《设置》应用程序

4.点击Device Screenshot按钮

5.点击”放大镜”按钮

6.查看Node Detail中的”resource-id”信息

image-20200608153720261

元素定位操作API

应用场景:

计算机需要元素定位才能知道点击哪个位置。

步骤:

  1. 打开uiautomatorviewer
  2. 打开模拟器
  3. 通过uiautomatorviewer获取node detail
  4. 通过元素定位api进行定位
  5. 进行元素的操作

定位一个元素

方法:

1
2
3
4
5
6
#通过id定位一个元素
#参数
# id_value:元素的resource-id属性值
#返回值:
# 定位到单个元素
driver.find_element_by_id(id_value)
1
2
3
4
5
6
#通过id定位一个元素
#参数
# class_value:元素的class属性值
#返回值:
# 定位到单个元素
driver.find_element_by_class_name(class_value)
1
2
3
4
5
6
#通过id定位一个元素
#参数
# xpath_value:定位元素的xpath表达式
#返回值:
# 定位到单个元素
driver.find_element_by_xpath(xpath_value)

示例:

通过id定位“放大镜“,并点击

通过class定位输入框,输入”hello”

通过xpath定位“返回”,并点击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from appium import webdriver
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

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

#id定位放大镜,点击
driver.find_element_by_id("com.android.settings:id/search").click()
#class定位输入框,输入
driver.find_element_by_class_name("android.widget.EditText").send_keys("hello")
#xpath定位返回,点击
driver.find_element_by_xpath("//*[@content-desc='收起']").click()

time.sleep(3)
driver.quit()

tips:

如果很多元素的“特征”相同的,使用find. element. .by. xx的方法会找到第一个

定位一组元素

应用场景:

批量操作某个相同特征的元素,使用定位一组的方式更方便

方法:

1
2
3
4
5
6
#通过id定位一组元素
#参数
# id_value:元素的resource-id的属性值
#返回值:
# 列表,定位到的所以符合的元素
driver.find_elements_by_id(id_value)
1
2
3
4
5
6
#通过class定位一组元素
#参数
# class_value:元素的class的属性值
#返回值:
# 列表,定位到的所以符合的元素
driver.find_elements_by_class_name(class_value)
1
2
3
4
5
6
#通过xpath定位一组元素
#参数
# xpath_value:元素的xpath的属性值
#返回值:
# 列表,定位到的所以符合的元素
driver.find_elements_by_xpath(xpath_value)

示例:

通过id的形式,获取所有resource-id 为”com.android.settings:id/title”的元素,并打印其文字内容

通过class_ name的形式,获取所有class 为”android.widget.TextView”的元素,并打印其文字内容

通过xpath的形式,获取所有包含”设”的元素,并打印其文字内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from appium import webdriver
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

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

#通过id的形式,获取所有resource-id 为"com.android.settings:id/title"的元素,并打印其文字内容
titles = driver.find_elements_by_id("com.android.settings:id/title")
print(len(titles))
for title in titles:
print(title.text)
#通过class_ name的形式,获取所有class 为"android.widget.TextView"的元素,并打印其文字内容
text_views = driver.find_elements_by_class_name("android.widget.TextView")
print(len(text_views))
for text_view in text_views:
print(text_view.text)
#通过xpath的形式,获取所有包含”设”的元素,并打印其文字内容
words = driver.find_elements_by_xpath("//*[contains(@text,'设')]")
print(len(words))
for word in words:
print(word.text)

time.sleep(3)
driver.quit()

定位元素的注意点

image-20200608163409158

元素等待

应用场景:

可能由于一些原因,我们想找到的元素并没有出来,会报错。

  1. 网速原因
  2. 服务器处理的请求原因
  3. 电脑配置的原因

概念:

webdriver定位页面元素如果未找到,会在指定时间内等待

元素等待分为:

1.显示等待

2.隐式等待

隐式等待

应用场景:

针对所有的定位元素的超时时间设置为同一个值的时候

概念:

等待元素加载指定的时长,超出的时长抛出NoSuchElementException异常

步骤:

获取driver对象后,使用driver调用implicitly_wait 方法即可

参数:

1
2
3
from selenium.webdriver.support.wait import webDriverwait
#seconds:等待秒数
driver.implicitly_wait(seconds)

示例:

20s内,在设置中找返回按钮,找到点击,没找到报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from appium import webdriver
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

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

driver.implicitly_wait(20)

print("--准备找返回--")
driver.find_element_by_xpath("//*[@content-desc='收起']").click()
print("--点完了--")

time.sleep(3)
driver.quit()

如果找到,之间进行后续操作

规定时间内没找到则报错

显示等待

应用场景:

针对所有的定位元素的超时时间设置为不同的时候

概念:

等待元素加载指定的时长,超出的时长抛出NoSuchElementException异常

步骤:

1.导包

2.创建WebDriverWait对象

3.调用WebDriverWait对象的until方法

示例:

5s内,每1秒在设置中找返回按钮,找到点击,没找到报错

核心代码:

image-20200608170547645

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from appium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

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


print("--准备找返回--")
wait = WebDriverWait(driver,5,poll_frequency=1)
click_button = wait.until(lambda x :x.find_element_by_xpath("//*[@content-desc='收起']"))
click_button.click()
print("--点完了--")

time.sleep(3)
driver.quit()

隐式等待和显示等待的选择

image-20200608172521989

元素操作API

点击元素

方法名:

1
2
#对element按钮进行点击操作
element.click()

输入和清空输入框

方法名:

1
2
3
4
5
6
#对element输入框进行输入操作
#参数:
#value:输入的内容
element.send_keys(value)
#对element输入框进行清空操作
element.clear()

tips:

默认输入中文是无效的,但不会报错,需要在前置代码增加两个参数

desired_caps [‘ unicodeKeyboard’] = True

desired_ caps [‘resetKeyboard’] = True

获取元素的文本内容

方法名:

1
2
3
4
#获取element的文本内容
#返回值
# 控件的文本内容
element.text

获取元素的位置和大小

方法名:

image-20200608173738176

获取元素的属性值

方法名:

1
2
3
4
5
6
#对element进行点击操作
#参数:
# value:要获取的属性名
#返回值:
# 根据属性名得到属性值
element.get_attribute(value)#value:元素的属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from appium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
import time

desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

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

eles = driver.find_elements_by_id("com.android.settings:id/title")

for i in eles:
print(i.get_attribute("enabled"))
time.sleep(3)
driver.quit()

tips:

image-20200608175016380

滑动和拖拽事件

应用场景:

我们在做自动化测试的时候,有些按钮是需要滑动几次屏幕后才会出现,此时,我们需要使用代码来模拟手指的滑动,也就是我们将要学习的滑动和拖拽事件。

swipe滑动事件

概念:

从一个坐标滑动到另外一个坐标位置,只能是两个点之间的滑动。

方法名:

1
2
3
4
5
6
7
#参数:
# start_x:起点x
# start_y:起点y
# end_x:终点x
# end_y:终点y
# duration:滑动操作的持续时间,单位:ms
driver.swipe(start_x,start_y,end_x,end_y,duration=None)

tips:

距离相等时,持续时间越短,惯性越大

持续时间相等时,滑动距离越大,实际滑动距离也越大

scroll滑动事件

概念:

从一个元素滑动到另一个元素,直到页面自动停止

方法名

1
2
3
4
#参数:
# origin_el: 滑动开始的元素
# destination: 滑动解释的元素
driver.scroll(origin_el,destination_el)

示例:

1
2
3
save_button = driver.find_element_by_xpath("//*[@text='存储']")
more_button = driver.find_element_by_xpath("//*[@text='更多']")
driver.scroll(save_button,more_button)

tips:

不能设置持续事件,惯性大

drag_and_drop拖拽事件

概念:

从一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置

方法名:

1
2
3
4
#参数:
# origin_el: 滑动开始的元素
# destination: 滑动解释的元素
driver.drag_and_drop(origin_el,destination_el)

示例:

1
2
3
save_button = driver.find_element_by_xpath("//*[@text='存储']")
more_button = driver.find_element_by_xpath("//*[@text='更多']")
driver.drag_and_drop(save_button,more_button)

tips:

不能设置持续时间,没有惯性

滑动和拖拽事件的选择

滑动和拖拽无非就是考虑是否有惯性,以及传递的参数是‘元素’还是‘坐标’

分为以下四种情况:

  • 有惯性,传入元素

    scroll

  • 无惯性,传入元素

    drag_and_drop

  • 有惯性,传入坐标

    swipe,并设置较短duration时间

  • 无惯性,传入坐标

    swipe,并设置较长duration时间

高级手势TouchAction

应用场景:

TouchAction可以实现一些针对手势的操作,比如滑动、长按、拖动等。我们可以将这些基本手势组合成-一个相对复杂的手势。比如,我们解锁手机或者一些应用软件都有手势解锁的这种方式。

使用步骤:

  1. 创建TouchAction对象
  2. 通过对象调用想执行的手势
  3. 通过perform()执行动作

手指轻巧操作

应用场景:

模拟手指对某个元素或坐标按下并快速抬起。比如,固定点击(100, 100)的位置。

方法名:

1
2
3
4
5
#参数
# element:元素
# x:x坐标
# y:y坐标
TouchAction(driver).tap(element=None,X=None,Y=None,count=1).perform()

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from appium import webdriver
import time
from appium.webdriver.common.touch_action import TouchAction
desired_caps = dict()
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '5.1'
desired_caps['deviceName'] = '192.168.79.102:5555'
desired_caps['appPackage'] = 'com.android.settings'
desired_caps['appActivity'] = '.Settings'

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

wlan_button = driver.find_element_by_xpath("//*[@text='WLAN']")
TouchAction(driver).tap(wlan_button).perform()

time.sleep(3)
driver.quit()

按下和抬起的操作

应用场景:

模拟手指一直按下,模拟手指抬起。可以模拟轻巧和长按操作

方法名:

1
2
3
4
5
6
#按下
#参数
# element:元素
# x:x坐标
# y:y坐标
TouchAction(driver).press(element=None,X=None,Y=None,count=1).perform()
1
2
#抬起
TouchAction(driver).release().perform()

等待操作

方法名:

1
2
3
4
#模拟手指暂停的操作
#参数:
#ms:毫秒
TouchAction(driver).wait(ms=0).perform()

长按操作

方法名:

1
2
3
4
5
6
#参数
# element:元素
# x:x坐标
# y:y坐标
# duration: 长按时长,毫秒
TouchAction(driver).long_press(element=None,X=None,Y=None,duration=0).perform()

移动操作

方法名:

1
2
3
4
5
#参数
# element:元素
# x:x坐标
# y:y坐标
TouchAction(driver).move_to(element=None,x=None,y=None).perform()

手机操作API

获取手机的分辨率

应用场景:

自动化测试可能会需要根据当前设备的屏幕分辨率来计算一些点击或者滑动的坐标。

方法名:

1
2
3
#获取手机分辨率
#返回值:字典{'height':800,'width'480}
driver.get_window_size()

手机截图

方法名:

1
2
3
4
#截图
#参数:
# filename:指定路径,指定格式
get_screenshot_as_file(filename)

示例

1.打开设置页面

2.截图保村screenshot.jpg

1
2
#截图
driver.get_screenshot_as_file("screenshot.jpg")

获取和设置手机网络

获取手机网络

方法名:

1
2
#获取手机网络
driver.network_connection

对照表:

image-20200608202840720

设置手机网络
1
2
#设置手机网络
driver.set_network_connection(connectionType)

tips:

image-20200608203303230

发送键到设备

应用场景:

模拟按“返回键””home键”等等操作,比如,很多应用有按两次返回键退出应用的功能,如果这个功能需要我们做自动化,那么一定会用到这个方法。

方法名:

1
2
3
4
#参数
# keycode:发送给设备的关键代码
# metastate:关于被发送代码的元信息,一般默认
driver.press_keycode(keycode,metastate=None)

tips:

按键对应编码

https://blog.csdn.net/feizhixuan46789/article/details/16801429

操作手机的通知栏

应用场景:

测试即时通信类软件的时候,如果A给B发送一条消息,B的通知栏肯定会显示对应的消息。我们想通过通知栏来判断B是否收到消息,一定要先操作手机的通知栏。

方法名:

1
2
#打开通知栏
driver.open_notifications()

tips:

appium官方并没有为我们提供关闭通知的api,那么现实生活中怎么关闭,就怎样操作就行,比如,手指从下往上滑动,或者,按返回键

示例:

1
2
3
4
#打开通知栏,按下返回键
driver.open_notifications()
time.sleep(3)
driver.press_keycode(4)
------- 本文结束  感谢您的阅读 -------