技术标签: APP自动化测试
跟selenium一样,appium也分为两种操作类型:单一操作、动作链条
跟selenium一样,appium中的webdriver中单独封装了一些单一、简单的操作,包括:滚动、拖拽、点击、滑动等。
class ActionHelpers(webdriver.Remote):
def scroll(self: T, origin_el: WebElement, destination_el: WebElement, duration: Optional[int] = None) -> T:
"""Scrolls from one element to another
Args:
origin_el: the element from which to being scrolling
destination_el: the element to scroll to
duration: a duration after pressing originalEl and move the element to destinationEl.
Default is 600 ms for W3C spec. Zero for MJSONWP.
Usage:
driver.scroll(el1, el2)
Returns:
Union['WebDriver', 'ActionHelpers']: Self instance
"""
# XCUITest x W3C spec has no duration by default in server side
if self.w3c and duration is None:
duration = 600
action = TouchAction(self)
if duration is None:
action.press(origin_el).move_to(destination_el).release().perform()
else:
action.press(origin_el).wait(duration).move_to(destination_el).release().perform()
return self
def drag_and_drop(self: T, origin_el: WebElement, destination_el: WebElement) -> T:
"""Drag the origin element to the destination element
Args:
origin_el: the element to drag
destination_el: the element to drag to
Returns:
Union['WebDriver', 'ActionHelpers']: Self instance
"""
action = TouchAction(self)
action.long_press(origin_el).move_to(destination_el).release().perform()
return self
def tap(self: T, positions: List[Tuple[int, int]], duration: Optional[int] = None) -> T:
"""Taps on an particular place with up to five fingers, holding for a
certain time
Args:
positions: an array of tuples representing the x/y coordinates of
the fingers to tap. Length can be up to five.
duration: length of time to tap, in ms
Usage:
driver.tap([(100, 20), (100, 60), (100, 100)], 500)
Returns:
Union['WebDriver', 'ActionHelpers']: Self instance
"""
if len(positions) == 1:
action = TouchAction(self)
x = positions[0][0]
y = positions[0][1]
if duration:
action.long_press(x=x, y=y, duration=duration).release()
else:
action.tap(x=x, y=y)
action.perform()
else:
ma = MultiAction(self)
for position in positions:
x = position[0]
y = position[1]
action = TouchAction(self)
if duration:
action.long_press(x=x, y=y, duration=duration).release()
else:
action.press(x=x, y=y).release()
ma.add(action)
ma.perform()
return self
def swipe(self: T, start_x: int, start_y: int, end_x: int, end_y: int, duration: int = 0) -> T:
"""Swipe from one point to another point, for an optional duration.
Args:
start_x: x-coordinate at which to start
start_y: y-coordinate at which to start
end_x: x-coordinate at which to stop
end_y: y-coordinate at which to stop
duration: time to take the swipe, in ms.
Usage:
driver.swipe(100, 100, 100, 400)
Returns:
Union['WebDriver', 'ActionHelpers']: Self instance
"""
# `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
def flick(self: T, start_x: int, start_y: int, end_x: int, end_y: int) -> T:
"""Flick from one point to another point.
Args:
start_x: x-coordinate at which to start
start_y: y-coordinate at which to start
end_x: x-coordinate at which to stop
end_y: y-coordinate at which to stop
Usage:
driver.flick(100, 100, 100, 400)
Returns:
Union['WebDriver', 'ActionHelpers']: Self instance
"""
action = TouchAction(self)
action \
.press(x=start_x, y=start_y) \
.move_to(x=end_x, y=end_y) \
.release()
action.perform()
return self
如下,需要从右往左滑动屏幕:
from appium import webdriver
from APP_project_v0.swipe_function import BasePage
caps = {
"platformName": "Android",
"deviceName": "emulator-5554",
"automationName": "UiAutomator1",
"appPackage": "cc.forestapp",
"appActivity": "cc.forestapp.applications.SplashActivity",
"noReset": False
}
driver = webdriver.Remote(
command_executor="http://127.0.0.1:4723/wd/hub",
desired_capabilities=caps
)
#获取手机窗口尺寸
size = driver.get_window_size()
#单独获取手机的宽度、高度
width = size["width"]
height = size["height"]
#等待可滑屏的界面出现
import time
time.sleep(9)
#连续滑动5次
i = 0
while(i < 5):
# 调用滑动方法,分别传入开始点的坐标及结束点的坐标
driver.swipe(
start_x=width * 0.9,
start_y=height * 0.5,
end_x=width * 0.1,
end_y=height * 0.5
)
i += 1
def swipe_left(self, duration=None):
"""向左边滑动"""
size = self.driver.get_window_size()
# 得到一个字典
width = size["width"]
height = size["height"]
# 向左滑动
# 这个就是滑动的标准操作
self.driver.swipe(
start_x=width * 0.9,
start_y=height * 0.5,
end_x=width * 0.1,
end_y=height * 0.5,
duration=duration
)
def swipe_right(self, duration=None):
size = self.driver.get_window_size()
# 得到一个字典
width = size["width"]
height = size["height"]
# 向左滑动
# 这个就是滑动的标准操作
self.driver.swipe(
start_x=width * 0.1,
start_y=height * 0.5,
end_x=width * 0.9,
end_y=height * 0.5,
duration=duration
)
def swipe_up(self, duration=None):
size = self.driver.get_window_size()
# 得到一个字典
width = size["width"]
height = size["height"]
# 向左滑动
# 这个就是滑动的标准操作
self.driver.swipe(
start_x=width * 0.5,
start_y=height * 0.9,
end_x=width * 0.5,
end_y=height * 0.1,
duration=duration
)
def swipe_down(self, duration=None):
size = self.driver.get_window_size()
# 得到一个字典
width = size["width"]
height = size["height"]
# 向左滑动
# 这个就是滑动的标准操作
self.driver.swipe(
start_x=width * 0.5,
start_y=height * 0.1,
end_x=width * 0.5,
end_y=height * 0.9,
duration=duration
)
from appium import webdriver
from APP_project_v0.swipe_function import BasePage
caps = {
"platformName": "Android",
"deviceName": "emulator-5554",
"automationName": "UiAutomator1",
"appPackage": "cc.forestapp",
"appActivity": "cc.forestapp.applications.SplashActivity",
"noReset": False
}
driver = webdriver.Remote(
command_executor="http://127.0.0.1:4723/wd/hub",
desired_capabilities=caps
)
base = BasePage(driver)
import time
time.sleep(9)
i = 0
while(i < 5):
base.swipe_right_to_left()
i += 1
driver.swipe()
在app使用过程中,我们可能需要用到虚拟键盘中的键位,或者手机上的按钮,比如:音量+、音量-、锁屏等。
在appium中,要进行键盘操作,可以调用press_keycode方法,传入一个数字,这个数字代表具体的键位。因此,当我们需要按某个按键时,需要上网去查按键所对应的数字是多少。
这里提供一篇文章可供参考:
https://blog.csdn.net/fantasyweirdo/article/details/86235206
举个例子,比如我们要按音量+,对应的键码是175:
接着就可以这样写:
#按音量+键
driver.press_keycode(175)
另外还有另一种更方便的方法,我们可以封装一个类:
class Keycode():
BACKSPACE = 8
TAB = 9
CLEAR = 12
ENTER = 13
当要使用按键时,则可以直接调用这个类下面的变量:
driver.press_keycode(Keycode.ENTER)
如果使用单一动作并不能完成整个操作,而需要多个连续的单一动作组合才能完成,需要使用动作链条,也就是TouchAction类。在调用时可进行链式调用:A动作().B动作().A动作().C动作().perform()
class TouchAction:
def __init__(self, driver: Optional['WebDriver'] = None):
self._driver = driver
self._actions: List = []
def tap(self: T, element: Optional['WebElement'] = None, x: Optional[int]
= None, y: Optional[int] = None, count: int = 1) -> T:
"""Perform a tap action on the element
Args:
element: the element to tap
x : x coordinate to tap, relative to the top left corner of the element.
y : y coordinate. If y is used, x must also be set, and vice versa
Returns:
`TouchAction`: Self instance
"""
opts = self._get_opts(element, x, y)
opts['count'] = count
self._add_action('tap', opts)
return self
def press(self: T, el: Optional['WebElement'] = None, x: Optional[int] = None,
y: Optional[int] = None, pressure: Optional[float] = None) -> T:
"""Begin a chain with a press down action at a particular element or point
Args:
el: the element to press
x: x coordiate to press. If y is used, x must also be set
y: y coordiate to press. If x is used, y must also be set
pressure: [iOS Only] press as force touch. Read the description of `force` property on Apple's UITouch class
(https://developer.apple.com/documentation/uikit/uitouch?language=objc) for more details on possible value ranges.
Returns:
`TouchAction`: Self instance
"""
self._add_action('press', self._get_opts(el, x, y, pressure=pressure))
return self
def long_press(self: T, el: Optional['WebElement'] = None, x: Optional[int]
= None, y: Optional[int] = None, duration: int = 1000) -> T:
"""Begin a chain with a press down that lasts `duration` milliseconds
Args:
el: the element to press
x: x coordiate to press. If y is used, x must also be set
y: y coordiate to press. If x is used, y must also be set
duration: Duration to press
Returns:
`TouchAction`: Self instance
"""
self._add_action('longPress', self._get_opts(el, x, y, duration))
return self
def wait(self: T, ms: int = 0) -> T:
"""Pause for `ms` milliseconds.
Args:
ms: The time to pause
Returns:
`TouchAction`: Self instance
"""
if ms is None:
ms = 0
opts = {
'ms': ms}
self._add_action('wait', opts)
return self
def move_to(self: T, el: Optional['WebElement'] = None, x: Optional[int] = None, y: Optional[int] = None) -> T:
"""Move the pointer from the previous point to the element or point specified
Args:
el: the element to be moved to
x: x coordiate to be moved to. If y is used, x must also be set
y: y coordiate to be moved to. If x is used, y must also be set
Returns:
`TouchAction`: Self instance
"""
self._add_action('moveTo', self._get_opts(el, x, y))
return self
def release(self: T) -> T:
"""End the action by lifting the pointer off the screen
Returns:
`TouchAction`: Self instance
"""
self._add_action('release', {
})
return self
def perform(self: T) -> T:
"""Perform the action by sending the commands to the server to be operated upon
Returns:
`TouchAction`: Self instance
"""
if self._driver is None:
raise ValueError('Set driver to constructor as a argument when to create the instance.')
params = {
'actions': self._actions}
self._driver.execute(Command.TOUCH_ACTION, params)
# get rid of actions so the object can be reused
self._actions = []
return self
@property
def json_wire_gestures(self) -> List[Dict]:
gestures = []
for action in self._actions:
gestures.append(copy.deepcopy(action))
return gestures
def _add_action(self, action: str, options: Dict) -> None:
gesture = {
'action': action,
'options': options,
}
self._actions.append(gesture)
def _get_opts(self, el: Optional['WebElement'] = None, x: Optional[int] = None, y: Optional[int] = None,
duration: Optional[int] = None, pressure: Optional[float] = None) -> Dict[str, Union[int, float]]:
opts = {
}
if el is not None:
opts['element'] = el.id
# it makes no sense to have x but no y, or vice versa.
if x is not None and y is not None:
opts['x'] = x
opts['y'] = y
if duration is not None:
opts['duration'] = duration
if pressure is not None:
opts['pressure'] = pressure
return opts
如下九宫格,需要在起始点长按,然后在屏幕上移动,直至移动到结束点松开:
这里是可以把九宫格进行切分的:
from appium import webdriver
from APP_project_v0.swipe_function import BasePage
from appium.webdriver.common.touch_action import TouchAction
caps = {
"platformName": "Android",
"deviceName": "emulator-5554",
"automationName": "UiAutomator1",
"appPackage": "com.xxzb.fenwoo",
"appActivity": "com.xxzb.fenwoo.activity.addition.WelcomeActivity",
"noReset": False
}
driver = webdriver.Remote(
command_executor="http://127.0.0.1:4723/wd/hub",
desired_capabilities=caps
)
# 定位到 activity
driver.start_activity(
app_package="com.xxzb.fenwoo",
app_activity=".activity.user.CreateGesturePwdActivity"
)
#进入九宫格界面
time.sleep(1)
e = driver.find_element_by_id('com.xxzb.fenwoo:id/right_btn')
e.click()
#九宫格元素定位,并获取九宫格的尺寸
time.sleep(2)
e = driver.find_element_by_id("com.xxzb.fenwoo:id/gesturepwd_create_lockview")
size = element.rect
#获取九宫格宽度、高度
width = size["width"]
height = size["height"]
#获取九宫格起始点坐标
start_x = size["x"]
start_y = size["y"]
#获取九宫格中每个点的相对坐标
point_1 = {
"x":start_x + width * 1 / 6 , "y": start_y + height * 1 /6}
point_2 = {
"x":start_x + width * 3 / 6 , "y": start_y + height * 1 /6}
point_3 = {
"x":start_x + width * 5 / 6 , "y": start_y + height * 1 /6}
point_4 = {
"x":start_x + width * 1 / 6 , "y": start_y + height * 3 /6}
point_5 = {
"x":start_x + width * 3 / 6 , "y": start_y + height * 3 /6}
point_6 = {
"x":start_x + width * 5 / 6 , "y": start_y + height * 3 /6}
point_7 = {
"x":start_x + width * 1 / 6 , "y": start_y + height * 5 /6}
point_8 = {
"x":start_x + width * 3 / 6 , "y": start_y + height * 5 /6}
point_9 = {
"x":start_x + width * 5 / 6 , "y": start_y + height * 5 /6}
#调用TouchAction进行操作
action = TouchAction(driver)
action.press(**point_1).wait(200).\
move_to(**point_2).wait(200).\
move_to(**point_5).wait(200).\
move_to(**point_8).wait(200).\
move_to(**point_9).release().perform()
time.sleep(3)
driver.quit()
def jiugongge(self, locator, pos: list):
"""九宫格。
pos = [2,3,5,7]
"""
el = self.find_element(locator)
size = el.rect
start_x = size["x"]
start_y = size["y"]
width = size["width"]
height = size["height"]
points = [
{
'x': start_x + width / 6 * 1, 'y': start_y + height / 6},
{
'x': start_x + width / 6 * 3, 'y': start_y + height / 6},
{
'x': start_x + width / 6 * 5, 'y': start_y + height / 6},
{
'x': start_x + width / 6 * 1, 'y': start_y + height / 6 * 3},
{
'x': start_x + width / 6 * 3, 'y': start_y + height / 6 * 3},
{
'x': start_x + width / 6 * 5, 'y': start_y + height / 6 * 3},
{
'x': start_x + width / 6 * 1, 'y': start_y + height / 6 * 5},
{
'x': start_x + width / 6 * 3, 'y': start_y + height / 6 * 5},
{
'x': start_x + width / 6 * 5, 'y': start_y + height / 6 * 5},
]
touch = TouchAction(self.driver)
# 传入的参数是从 0 开始的。
# pos = [2,3,5,7]
# 索引 = 位置 - 1
touch.press(**points[pos[0] - 1]).wait(200)
for p in pos[1:]:
touch.move_to(**points[p - 1]).wait(200)
touch.release().perform()
如果需要多个手指进行操作,比如:放大、缩小等,就需要用到MultiAction类。
class MultiAction:
def __init__(self, driver: 'WebDriver', element: Optional['WebElement'] = None) -> None:
self._driver = driver
self._element = element
self._touch_actions: List['TouchAction'] = []
def add(self, *touch_actions: 'TouchAction') -> None:
"""Add TouchAction objects to the MultiAction, to be performed later.
Args:
touch_actions: one or more TouchAction objects describing a chain of actions to be performed by one finger
Usage:
| a1 = TouchAction(driver)
| a1.press(el1).move_to(el2).release()
| a2 = TouchAction(driver)
| a2.press(el2).move_to(el1).release()
| MultiAction(driver).add(a1, a2)
Returns:
`MultiAction`: Self instance
"""
for touch_action in touch_actions:
if self._touch_actions is None:
self._touch_actions = []
self._touch_actions.append(copy.copy(touch_action))
def perform(self: T) -> T:
"""Perform the actions stored in the object.
Usage:
| a1 = TouchAction(driver)
| a1.press(el1).move_to(el2).release()
| a2 = TouchAction(driver)
| a2.press(el2).move_to(el1).release()
| MultiAction(driver).add(a1, a2).perform()
Returns:
`MultiAction`: Self instance
"""
self._driver.execute(Command.MULTI_ACTION, self.json_wire_gestures)
# clean up and be ready for the next batch
self._touch_actions = []
return self
@property
def json_wire_gestures(self) -> Dict[str, Union[List, str]]:
actions = []
for action in self._touch_actions:
actions.append(action.json_wire_gestures)
if self._element is not None:
return {
'actions': actions, 'elementId': self._element.id}
return {
'actions': actions}
由上面源码可以看出,每一个手指头代表一个TouchAction的对象,每个TouchAction对象都会去调用一系列的动作。
比如说,一个图片,需要放大、缩小;一个地图,需要放大、缩小。
#放大
action_1 = TouchAction(driver)
action_1.press(x=width / 2, y=height / 2).move_to(
x=width / 2, y=height / 2 - 500).release()
action_2 = TouchAction(driver)
action_2.press(x=width / 2, y=height / 2).move_to(
x=width / 2, y=height / 2 + 500).release()
m = MultiAction(driver)
m.add(action_1, action_2)
m.perform()
ACM网站刚刚发布消息,出生于英国的理论计算科学家、哈佛大学教授Leslie Valiant因为“对众多计算理论(包括PAC学习、枚举复杂性、代数计算和并行与分布式计算)所做的变革性的贡献”而获得最新一届计算机科学最高荣誉——图灵奖。ACM的颁奖词高度评价了Valiant教授30多年对理论计算科学的基础性贡献:他的工作开辟了新领域,推出了独创的新概念,并提供了许多兼..._le'slie valiant
Nginx 做为代理服务器注意文件上传配置这块参数。否则会出现上传有时成功,有时失败的情况。千万记得设置。
《数据库基础及实践技术——SQL Server 2008》一3.6 分离和附加数据库3.6 分离和附加数据库利用分离和附加数据库的操作可以实现将数据库从一台计算机移动到另一台计算机,或者从一个实例移动到另一个实例的目的。数据库被分离后,其所包含的数据文件和日志文件不再受数据库管理系统的管理,因此,用户可以复制或剪切该数据库的全部文件,然后将它们放置到另一台计算机上,或者本计算机...文章华章计算机...
1. ACM国际大学生程序设计竞赛简介1) 背景与历史1970年在美国TexasA&M大学举办了首次区域竞赛,从而拉开了国际大学生程序设计竞赛的序幕。1977年,该项竞赛被分为两个级别:区域赛和总决赛,这便是现代ACM竞赛的开始。在亚洲、美国、欧洲、太平洋地区均设有区域赛点。1995至1996年,来自世界各地的一千多支s代表队参加了ACM区域竞赛。ACM大学生程序设计竞赛由美国计算机协会(ACM)举办,旨在向全世界的大学生提供一个展示和锻炼
改编@ToonSuperLove的答案,主要是为了避免NPE。public class WifiTest extends Activity {public void loadWifiAvailableList(EditText etWifiList) {WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERV...
Android 工具类Util 之 Toast新建类名ToastUtil//context 传入上下文 msg 传入显示文本public static void showToast(Context context, String msg) { Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); }
文章目录1. 升级python2. 安装python相关开发包3. 安装python模块4. ansible安装5. SSH免密钥登录设置6. 测试ansible介绍Ansible是一种批量部署工具,基用python开发的。ansible其实准确的说只提供了一个框架,它要基于很多其他的python模块才能工作的,所以在安装ansible的时候你要再装很多其他的依赖包的。好处之一是使用者可..._could not find suitable distribution for requirement.parse('cryptography')
比赛题目:学术论文分类挑战赛比赛链接:https://challenge.xfyun.cn/topic/info?type=academic-paper-classification&am..._讯飞 文本分类
ESMM: Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate 论文总结本文介绍 阿里妈妈团队 发表在 SIGIR’2018 的论文《Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate》。文章基于 Multi-Task Learning _pctr论文
运算符分为:1.算术运算符2.条件运算符3.逻辑运算符4.位运算符5.三元运算符在Lua中不支持位运算符和三位运算符,但是可以间接实现三元运算符1.算术运算符(+、-、*、/、%、^(幂运算符))注意:Lua中没有++,–,+=,-=,*=,/=,%=2.条件运算符(and ,or,not) 注意:Lua中的条件运算符就这三个,没有&&,||,!=3.逻辑运算符(> ,<, =, >=, <=, ==,~=) 注意:Lua中的不等于是"~="pri_lua &&
链表实现优先队列
proguard.config=proguard.cfg -dontwarn -dontnote-keep public class net.youmi.android.AdView 常见问题处理:(来自网络)原文:http://blog.csdn.net/aa4790139/article/details/6754230 第一种情况:Proguar..._混淆lua代码