书接上回,Appium高级操作--其他操作-CSDN博客文章浏览阅读182次,点赞6次,收藏7次。书接上回Appium高级操作--从源码角度解析--模拟复杂手势操作-CSDN博客。https://blog.csdn.net/fantasy_4/article/details/146162851主要讲解了Appium的一些高级操作,比如基于ActionChain类的,操作系统API的方法等,便于解决比较复杂的场景下的手势模拟。不过在自动化的过程中,经常会出现寻找查找元素时间过长,等待时间设置不合理导致脚本执行时间过程,最终的结果就是自动化运行的速度不如'手工'操作。为了解决上述问题,本篇文章主要讲解一下Appium的等待机制
1.影响页面加载时长的因素
做UI自动化的同学可能多数都会遇到这样的问题,执行跳转页面后再去定位,经常会提示找不到元素,造成找不到元素的原因除了有元素定位本身的问题之外,还可能的原因是页面本身的加载时长过长,那影响页面加载时长的因素有哪些呢?
-
移动端性能
市面上出现生产使用的移动端各不相同,不同的配制,不同的机型,不同的系统,甚至不同的操作系统版本,因此在这些设备上安装同一款软件就会产生不同的加载时长。如果调试脚本时使用的是一款配置较好的手机,而实际运行脚本的是另外的配置较差的手机,就会出现明明调试的时候没问题,正式运行就会出错。
-
服务端性能
如果执行自动化的服务端还部署着其他服务,比如缺陷管理工具、代码管理工具等,这样服务端存在大量并发用户请求,就会造成自动化执行时会花费更多的时间才能相应。
-
网络因素
如果被测试APP中的Web页面包含大量图片,或者请求中存在低劣无效的代码就会产生大量的数据请求,这是网络的稳定至关重要。
2.强制等待
所谓强制等待,就是在执行自动化的过程中加上一个强制的等待时间,等待时间结束后期望要跳转的页面能够加载完毕,如果没有那可能还是要调整时间。
通常使用Python time
模块的time.sleep(s)
来实现
优点:调试代码时,能够便于我们观察脚本的执行情况
缺点:强制等待会导致执行脚本的时间不一致,设置的等待时间参差不齐,尤其是脚本很多时,就会造成脚本越执行越慢。
那么有没有一种更智能的等待方式呢?能够实现找到元素就立刻进行下一步操作,如果找不到元素就进行等待呢?
3.隐性等待
隐性等待就能够完美解决上面提出的问题。
Appium的隐性等待继承了Selenium的implicitly_wait()
方法。
driver.implicitly_wait(5)
# 隐性等待5s
优点:可以很智能判断是否需要执行相应等待时长,一旦设置就会实例化整个会话的生命周期
缺点:会减缓测试速度(尤其是在需要查找某个元素不存在时的用例,会平白浪费时间等待查找该元素是不是存在),而且会干扰显性等待,不建议使用隐形等待和显性等待混用
4.显性等待
显性等待能够更精细化的定制一些执行条件,等到条件满足会后在进行下一步操作。
Appium并没有引入Selenium的WebDriverWait类,因此要使用显性等待,只能从Selenium中引入,主要由以下两部分实现显性等待
1)Selenium中的WebDriver类:定义超时时间、轮询频率等
2)Expected_conditions模块:提供一些预期条件作为测试脚本进行后续操作的判断依据
-
显性等待整体语法结构
WebdriverWait(dirver, 超时时间,轮询频率, 忽略异常).until(可执行方法,超时后返回的信息)
上面代码的含义:每隔一段时间,一定频次,就会调用可执行方法,直到方法返回True,如果超时则返回超时后的信息。
注意until中的可执行方法必须是可以调用的,即这个对象一定有__call__()
方法
-
WebDriverWait类源码分析
def __init__(self,driver: D,timeout: float,poll_frequency: float = POLL_FREQUENCY,ignored_exceptions: Optional[WaitExcTypes] = None, ):"""Constructor, takes a WebDriver instance and timeout in seconds. Attributes:----------driver- Instance of WebDriver (Ie, Firefox, Chrome or Remote) ora WebElement timeout- Number of seconds before timing out poll_frequency- Sleep interval between calls- By default, it is 0.5 second. ignored_exceptions- Iterable structure of exception classes ignored during calls.- By default, it contains NoSuchElementException only.
WebDriverWait类创建对象可以传入4个参数:
-
driver:WebDriver实例化-必传
-
timeout:超时时间-必传
-
poll_frequency:轮询频率-非必传
-
ignored_exceptions:可忽略异常-非必传
WebDriverWait类提供两个方法until()
和until_not()
,两个方法传参相同
-
method:可以调用方法-必传,
-
str: 异常信息-非必传
两个方法的含义不同:
until | until_not | |
---|---|---|
等待逻辑 | 等待条件变为 True | 等待条件变为 False 。 |
适用场景 | 等待元素出现或满足特定条件。 | 等待元素消失或不满足特定条件。 |
异常处理 | 如果条件未变为 True ,抛出 TimeoutException | 如果条件未变为 False ,抛出 TimeoutException |
返回值 | 返回满足条件的对象(如元素) | 无返回值(仅等待条件变为 False ) |
(使用的Selenium是配套Appium-Python-Client一起下载的,版本是 4.29.0,其中until_not的方法注释(定义部分)有错误)如下图
-
Expected_conditions模块
Expected_conditions模块是selenium提供的各种预期条件,Expected_conditions有多种方法,黑体字相对比较常用
方法 | 描述 |
---|---|
title_is(title: str) | 判断页面的title和预期title是否一致,一直则返回True,否则返回False |
title_contains(title: str) | 判断页面的title是否包含预期title是否一致,大小写敏感一直则返回True,否则返回False |
presence_of_element_located(locator: Tuple[str, str]) | 用于检查某个元素是否存在于DOM中,但不一定可见,一旦找到元素则返回WebElement |
presence_of_all_elements_located(locator: Tuple[str, str]) | 用于检查所有元素是否存在,如果存在返回所有匹配的元素的列表,否则报错 |
url_matches(pattern: str) | 检查当前driver的url是否包含字符串,包含返回True,不包含返回False |
url_to_be(url: str) | 检查当前driver的url与预期值是否完全匹配,匹配返回True,否则返回False |
url_changes(url: str) | 检查当前url和预期值是否一致,不一致返回True,一直返回False |
url_contains(url: str) | 检查当前driver的url是否包含字符串,包含返回True,不包含返回False |
visibility_of(element: WebElement) | 参数是WebElement,判断元素是否在当前页面的DOM中并可见,若是返回True,否则返回False |
visibility_of_element_located(locator: Tuple[str, str] ) | 参数是locator,判断元素是否在当前页面的DOM中并可见,若是返回True,否则返回False |
visibility_of_any_elements_located(locator: Tuple[str, str]) | 至少能定位到一个可见元素,是返回列表 否则报错 |
visibility_of_all_elements_located(locator: Tuple[str, str] ) | 找到所有符合条件的可见元素,是返回列表 否则报错 |
invisibility_of_element_located(locator: Union[WebElement, Tuple[str, str]] ) | 判断不可见元素是否存在 |
invisibility_of_element(element: Union[WebElement, Tuple[str, str]] ) | 判断元素是都不可见 |
staleness_of(element: WebElement) | 判断刷新后,元素是否仍然在DOM中,如果在返回False,否则返回True |
frame_to_be_available_and_switch_to_it(locator: Union[Tuple[str, str], str]) | 判断frame_locator是否存在,存在则跳转到对应frame并返回True,否则返回False |
text_to_be_present_in_element(locator: Tuple[str, str], text_: str) | 判断text是否出现在元素中 |
text_to_be_present_in_element_value(locator: Tuple[str, str], text_: str ) | 判断text是否出现在元素的value属性中 |
text_to_be_present_in_element_attribute(locator: Tuple[str, str], attribute: str, text: str ) | 判断text是否出现在元素的属性中 |
element_to_be_clickable(mark: Union[WebElement, Tuple[str, str]] ) | 检查元素是否可见并可以被单击并且单击 |
element_to_be_selected(element: WebElement) | 入参为WebElement,被定位的元素是否是被选中的,返回布尔值 |
element_located_to_be_selected(locator: Tuple[str, str]) | 入参为locator,被定位的元素是否是被选中的,返回布尔值 |
element_selection_state_to_be(element: WebElement, is_selected: bool) | 检查给定元素是否是被选中的 |
element_located_selection_state_to_be(locator: Tuple[str, str], is_selected: bool ) | 查找元素并检查指定的选择状态是否处于该状态的期望,返回布尔值 |
new_window_is_opened(current_handles: List[str]) | 传入当前窗口的句柄,判断是否有新窗口打开,返回布尔值 |
number_of_windows_to_be(num_windows: int) | 判断窗口数量是否符合预期 |
alert_is_present() | 判断是否有alert,如果有,切换到alert,否则返回False |
-
代码演示
from appium import webdriver from appium.options.android import UiAutomator2Options from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions desired_caps = {"platformName": "Android","deviceName": "XXXXXXXXXXXXX","appPackage": "com.sankuai.movie","appActivity": "com.sankuai.movie.MovieMainActivity","automationName": "UiAutomator2" } print("Desired Capabilities: ", desired_caps) driver = webdriver.Remote("http://localhost:4723", options=UiAutomator2Options().load_capabilities(desired_caps)) try:agree_id = "com.sankuai.movie:id/cyf"ele1 = WebDriverWait(driver, 10).until(expected_conditions.presence_of_element_located((AppiumBy.ID, agree_id)))ele1.click()my_id = 'com.sankuai.movie:id/b50'ele2 = WebDriverWait(driver, 10).until(expected_conditions.invisibility_of_element_located((AppiumBy.ID, my_id)))# WebDriverWait(driver, 10).until_not()ele2.click()ele4 = WebDriverWait(driver,10).until(expected_conditions.element_to_be_clickable((AppiumBy.ID, 'com.sankuai.movie:id/b50')))ele4.click() except Exception as e:raise e finally:# 关闭 Appium 会话driver.quit()
-
自定义等待条件
就是在expected_conditions模块不满足个需求的情况下,可以使用lambda表达式来自定义等待条件。
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions
ele3 = WebDriverWait(driver, 10).until(lambda driver: driver.find_elements(AppiumBy.ID, 'com.sankuai.movie:id/b50'))
ele3.click()
5.总结三种等待区别
强制等待 | 隐性等待 | 显性等待 | |
---|---|---|---|
实现方式 | time.sleep(3) | driver.implicitly_wait(5) | Webdriver类+expected_conditions模块 |
灵活程度 | 不管是否找元素,必须等待响应时间 | 找到元素则不等待,否则等待 | 在固定之间 |
生命周期 | 当前行 | 整个会话 | 每个条件需要单独设置 |
使用场景 | 脚本调试 | 页面加载时间相对固定的全局等待场景,不建议跟显性等待混用 | 动态页面或需要等待特定条件的复杂场景,建议多使用 |
下一章介可能会讲解一些关于自动化框架搭建相关内容,可以期待一下哦~