您的位置:首页 > 其它

小白也能学会的模拟屏幕滑动与手势密码绘制

2019-08-07 10:16 239 查看

前言

App自动化测试中有两个很重要的操作,屏幕滑动与绘制手势密码。目前很多App在启动时,都存在启动时的引导动画或者加载上下文内容时需要手动上滑或者下滑加载页面,所以在自动化测试的过程中模拟手的滑动操作看起来就很重要了;第二个比较重要的是模拟手动绘制九宫格完成手势密码的设置,这种手势密码在我了解的范围内,大多在金融类的app中最常见,还有一些对用户信息保密性较好的app中,所以,模拟绘制手势密码也是app自动化测试中必须掌握的操作,那么接下来我们就开始讲解两种操作该如何实现, 在进入正题之前,你还应该知道,手机中横纵坐标的原点是从屏幕的左上角顶点(0, 0)的位置开始的

滑动屏幕

swipe方法

模拟滑动屏幕的操作,我们通过swipe方法实现,先看一下这个方法的源代码

def swipe(self, start_x, start_y, end_x, end_y, duration=None):
"""Swipe from one point to another point, for an optional duration.

Args:
start_x (int): x-coordinate at which to start
start_y (int): y-coordinate at which to start
end_x (int): x-coordinate at which to stop
end_y (int): y-coordinate at which to stop
duration (:obj:`int`, optional): time to take the swipe, in ms.

Usage:
driver.swipe(100, 100, 100, 400)

Returns:
`WebElement`
"""
# `swipe` is something like press-wait-move_to-release, which the server
# will translate into the correct action
action = TouchAction(self)
action \
.press(x=start_x, y=start_y) \
.wait(ms=duration) \
.move_to(x=end_x, y=end_y) \
.release()
action.perform()
return self

参数

start_x, start_y : 表示开始滑动时的初始坐标,也就是从哪里开始滑动

end_x,   end_y : 表示滑动后的坐标,也就是滑动到哪里

duration:     : 表示滑动过程的时间间隔,模拟操作时,我们最好设置个时间间隔,避免由于代码运行太快,而真机或者模拟器反应比较慢,而操作失败,单位以毫秒计算

通过源码,我们发现swipe方法实际上是使用TouchAction实现的,这个类在后面我们仍然会使用,主要是模拟一些触屏动作

实现思路

大家可以想象一下,平时我们滑动屏幕时,是如何操作的?例如向左滑动屏幕,我们往往是把手放在屏幕的右侧,然后按住屏幕向左滑动,那么代码如何知道我们从屏幕的哪个位置开始讷?那就是坐标了,我们可以先获取屏幕的宽,高,然后按照它的比例计算鼠标的位置坐标,我这里取的起始坐标点为屏幕宽度的0.9倍,高度的0.5倍,大概就是我们实际中滑屏时手指的操作位置。大家可以根据下面播放的动画观察鼠标开始的大概位置和结束位置

def gesture_password(self, element: WebElement, *pwd):
"""手势密码: 直接输入需要链接的点对应的数字,最多9位
pwd: 1, 2, 3, 6, 9
"""
if len(pwd) > 9:
raise ValueError("需要设置的密码不能超过9位!")
keys_dict = self.get_password_location(element)
action = TouchAction(self.driver)
action.press(x=keys_dict[pwd[0]]["x"], y=keys_dict[pwd[0]]["y"]).wait(200)
for index in range(len(pwd)-1):  # 0,1,2,3
action.move_to(x=keys_dict[pwd[index+1]]["x"] - keys_dict[pwd[index]]["x"],
y=keys_dict[pwd[index+1]]["y"] - keys_dict[pwd[index]]["y"]).wait(200)
return action.release().perform()
2019.8.8修改 比较难理解的地方,我已经详细注释了,当然,你可以复制我的代码先验证能否绘制成功再分析代码的实现原理

所有代码

修改后的绘制手势密码代码&滑屏代码

"""
------------------------------------
@Time : 2019/8/6 20:45
@Auth : linux超
@File : base.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import time
from appium.webdriver import WebElement
from appium.webdriver.common.touch_action import TouchAction
from appium.webdriver.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.common.exceptions import NoSuchElementException, TimeoutException

class Base(object):

def __init__(self, driver: WebDriver):
self.driver = driver

@property
def get_phone_size(self):
"""获取屏幕的大小"""
width = self.driver.get_window_size()['width']
height = self.driver.get_window_size()['height']
return width, height

def swipe_left(self, duration=300):
"""左滑"""
width, height = self.get_phone_size
start = width * 0.9, height * 0.5
end = width * 0.1, height * 0.5
return self.driver.swipe(*start, *end, duration)

def swipe_right(self, duration=300):
"""右滑"""
width, height = self.get_phone_size
start = width * 0.1, height * 0.5
end = width * 0.9, height * 0.5
return self.driver.swipe(*start, *end, duration)

def swipe_up(self, duration):
"""上滑"""
width, height = self.get_phone_size
start = width * 0.5, height * 0.9
end = width * 0.5, height * 0.1
return self.driver.swipe(*start, *end, duration)

def swipe_down(self, duration):
"""下滑"""
width, height = self.get_phone_size
start = width * 0.5, height * 0.1
end = width * 0.5, height * 0.9
return self.driver.swipe(*start, *end, duration)

def skip_welcome_page(self, direction, num=3):
"""
滑动页面跳过引导动画
:param direction:  str 滑动方向,left, right, up, down
:param num: 滑动次数
:return:
"""
direction_dic = {
"left": "swipe_left",
"right": "swipe_right",
"up": "swipe_up",
"down": "swipe_down"
}
time.sleep(3)
if hasattr(self, direction_dic[direction]):
for _ in range(num):
getattr(self, direction_dic[direction])()  # 使用反射执行不同的滑动方法
else:
raise ValueError("参数{}不存在, direction可以为{}任意一个字符串".
format(direction, direction_dic.keys()))

@staticmethod
def get_element_size_location(element):
width = element.rect["width"]
height = element.rect["height"]
start_x = element.rect["x"]
start_y = element.rect["y"]
return width, height, start_x, start_y

def get_password_location(self, element: WebElement) -> dict:
width, height, start_x, start_y = self.get_element_size_location(element)
point_1 = {"x": int(start_x + width * (1 / 6) * 1), "y": int(start_y + height * (1 / 6) * 1)}
point_2 = {"x": int(start_x + width * (1 / 6) * 3), "y": int(start_y + height * (1 / 6) * 1)}
point_3 = {"x": int(start_x + width * (1 / 6) * 5), "y": int(start_y + height * (1 / 6) * 1)}
point_4 = {"x": int(start_x + width * (1 / 6) * 1), "y": int(start_y + height * (1 / 6) * 3)}
point_5 = {"x": int(start_x + width * (1 / 6) * 3), "y": int(start_y + height * (1 / 6) * 3)}
point_6 = {"x": int(start_x + width * (1 / 6) * 5), "y": int(start_y + height * (1 / 6) * 3)}
point_7 = {"x": int(start_x + width * (1 / 6) * 1), "y": int(start_y + height * (1 / 6) * 5)}
point_8 = {"x": int(start_x + width * (1 / 6) * 3), "y": int(start_y + height * (1 / 6) * 5)}
point_9 = {"x": int(start_x + width * (1 / 6) * 5), "y": int(start_y + height * (1 / 6) * 5)}
keys = {
1: point_1,
2: point_2,
3: point_3,
4: point_4,
5: point_5,
6: point_6,
7: point_7,
8: point_8,
9: point_9
}
return keys

def gesture_password(self, element: WebElement, *pwd):
"""手势密码: 直接输入需要链接的点对应的数字,最多9位
pwd: 1, 2, 3, 6, 9
"""
if len(pwd) > 9:
raise ValueError("需要设置的密码不能超过9位!")
keys_dict = self.get_password_location(element)
start_point = "TouchAction(self.driver).press(x={0}, y={1}).wait(200)". \
format(keys_dict[pwd[0]]["x"], keys_dict[pwd[0]]["y"])
for index in range(len(pwd) - 1):  # 0,1,2,3
follow_point = ".move_to(x={0}, y={1}).wait(200)". \
format(keys_dict[pwd[index + 1]]["x"] - keys_dict[pwd[index]]["x"],
keys_dict[pwd[index + 1]]["y"] - keys_dict[pwd[index]]["y"])
start_point = start_point + follow_point
full_point = start_point + ".release().perform()"
return eval(full_point)

def find_element(self, locator: tuple, timeout=30) -> WebElement:
wait = WebDriverWait(self.driver, timeout)
try:
element = wait.until(lambda driver: driver.find_element(*locator))
return element
except (NoSuchElementException, TimeoutException):
print('no found element {} by {}', format(locator[1], locator[0]))

if __name__ == '__main__':
pass
base.py
"""
------------------------------------
@Time : 2019/8/6 20:47
@Auth : linux超
@File : test.py
@IDE  : PyCharm
@Motto: Real warriors,dare to face the bleak warning,dare to face the incisive error!
@QQ   : 28174043@qq.com
@GROUP: 878565760
------------------------------------
"""
import time
import unittest
from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy

from base import Base

class TestGesture(unittest.TestCase):

def setUp(self):
desired = {
"automationName": "uiautomator1",
"platformName": "Android",
"platformVersion": '5.1.1',
"deviceName": "127.0.0.1:62001",
"appPackage": "com.xxzb.fenwoo",
"appActivity": "com.xxzb.fenwoo.activity.addition.WelcomeActivity",
"app": r"D:\AppAutoTest\appPackage\Future-release-2018.apk",
"unicodeKeyboard": True,  # 屏蔽键盘
"resetKeyboard": True
}
self.driver = webdriver.Remote(command_executor="http://127.0.0.1:4723/wd/hub",
desired_capabilities=desired)
self.base = Base(self.driver)

def test_gesture_password(self):
self.base.skip_welcome_page('left', 3)  # 滑动屏幕
time.sleep(3)  # 为了看滑屏的效果
self.driver.start_activity(app_package="com.xxzb.fenwoo",
app_activity=".activity.user.CreateGesturePwdActivity")
commit_btn = (MobileBy.ID, 'com.xxzb.fenwoo:id/right_btn')
password_gesture = (MobileBy.ID, 'com.xxzb.fenwoo:id/gesturepwd_create_lockview')
element_commit = self.base.find_element(commit_btn)
element_commit.click()
password_element = self.base.find_element(password_gesture)
self.base.gesture_password(password_element, 1, 2, 3, 6, 5, 4, 7, 8, 9)
time.sleep(5)  # 看效果

def tearDown(self):
self.driver.quit()

if __name__ == '__main__':
unittest.main()
test.py

测试效果

包含滑动屏幕

总结

最后,我们再总结一下完成所有的操作需要掌握的知识点

1.滑动屏幕时起始位置和结束位置应该从哪里开始与结束,如何获取

2.滑动屏幕使用的swipe()方法如何使用

3.实现多次滑动方法的实现原理,这里用到了反射,其实使用if也可以实现一样的效果,但是总感觉if有点low

4.9宫格起始位置与手机屏幕的关系及每个点的坐标如何计算

5.TouchAction类中的常用方法如何使用

6.理解绘制手势密码方法的封装原理及思路

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: