小白也能学会的模拟屏幕滑动与手势密码绘制
前言
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__': passbase.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.理解绘制手势密码方法的封装原理及思路
- Android 滑动切换页面 以及屏幕手势
- Android 屏幕手势滑动中onFling()函数的技巧分析
- [转]Android 滑动切换页面 以及屏幕手势
- 屏幕滑动手势GestureDetector
- [android] 手机卫士手势滑动切换屏幕
- Android 屏幕手势滑动中onFling()函数的技巧分析
- [android] 手机卫士手势滑动切换屏幕
- ViewFlipper手势滑动改变标题和AsyncTask模拟的滑动菜单 ..
- Android 滑动切换页面 以及屏幕手势
- IOS中常用手势:手指向右滑动和向左滑动屏幕实现固定操作
- Win10系统如何设置图片密码滑动手势登陆
- Android 屏幕滑动手势中onFling()函数分析
- 手势密码绘制
- Android 滑动切换页面 以及屏幕手势
- Android 滑动切换页面 以及屏幕手势
- Android 滑动切换页面 以及屏幕手势
- Android 滑动切换页面 以及屏幕手势
- Android手势识别——上下左右滑动、屏幕上下左右中区域处理
- Android 滑动切换页面 以及屏幕手势
- Android 屏幕手势滑动中onFling()函数的技巧分析