Appium等待机制--强制等待、隐式等待、显式等待

devtools/2025/3/14 18:32:01/

书接上回,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: 异常信息-非必传

两个方法的含义不同:

 untiluntil_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
  • 代码演示

    python">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表达式来自定义等待条件。

python">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模块
灵活程度不管是否找元素,必须等待响应时间找到元素则不等待,否则等待在固定之间
生命周期当前行整个会话每个条件需要单独设置
使用场景脚本调试页面加载时间相对固定的全局等待场景,不建议跟显性等待混用动态页面或需要等待特定条件的复杂场景,建议多使用

下一章介可能会讲解一些关于自动化框架搭建相关内容,可以期待一下哦~

 


http://www.ppmy.cn/devtools/167084.html

相关文章

Unity Lerp和InverseLerp函数用处

我认为最大的用处就是缓冲刚体移动!!!它的作用是每次调用都返回一个 a (b - a) * t的值,所以只要给一个变化的t值,就可以得到一个适中移动速度的刚体,类似下面这种用法,…

大模型LLM基于PEFT的LoRA微调详细步骤---第一篇:模型下载篇

模型下载: HuggingFace官网:https://huggingface.co/ ---- 需要VPN 魔搭社区:https://modelscope.cn/home ---- 国内映射,不需要VPN 写在篇始:国内关注方法一即可。其余几种都需要VPN,而且在服务器下载…

AI是如何实现屏幕触控防水? 实测华为畅享70X

在日常使用手机中,我们难免会遇到一些意外情况,比如屏幕上溅到水、洗澡或做饭时手上沾水、下雨天手机屏幕上有雨滴残留时,很容易出现触摸不灵敏或一通乱发,尤其是户外工作者比如外卖员、快递员碰到雨天手机操作不灵敏,…

Gitlab报错:sudo: a password is required

在Runner上面执行Gitlab脚本的时候,如果使用到了sudo命令,就需要输入密码,我们可以在visudo中设置Runner的执行用户在使用sudo的时候,不输入密码。 sudo visudo 在末尾加入这一行: gitlab-runner ALL(ALL) NOPASSWD: ALL再次运行…

携程笔试 2025.3.13

1.诗 第一行1个字&#xff0c;之后每行字数增加1&#xff0c;输出每行第一列字符。 #include <bits/stdc.h> using namespace std; string s,ans; int main() {int d,idx;cin>>s;idx1;didx;for(int i0;i<s.size();i) {while(d) {if(didx) {anss[i];}d--;i;}id…

图论part3|101.孤岛的总面积、沉没孤岛、417. 太平洋大西洋水流问题

101. 孤岛的总面积 &#x1f517;&#xff1a;101. 孤岛的总面积思路&#xff1a;和昨天的岛的区别是&#xff1a;是否有挨着边的岛屿 所以可以先遍历四条边挨着的岛屿&#xff0c;把他们标记为非孤岛再计算其他岛屿当中的最大面积 代码&#xff1a;&#xff08;深度搜索&…

内网渗透之内网基础知识(一)

工作组 工作组:工作组是局域网中的一个概念&#xff0c;他是长久的资源管理模式。默认情况下使 用工作组方式进行资源管理&#xff0c;将不同的 computer 按照不同的要求分类到不同的组 域:用来描述一种架构&#xff0c;和“工作组”相对应&#xff0c;由工作组升级而来的高级…

用Deepseek写一个五子棋微信小程序

在当今快节奏的生活中&#xff0c;休闲小游戏成为了许多人放松心情的好选择。五子棋作为一款经典的策略游戏&#xff0c;不仅规则简单&#xff0c;还能锻炼思维。最近&#xff0c;我借助 DeepSeek 的帮助&#xff0c;开发了一款五子棋微信小程序。在这篇文章中&#xff0c;我将…