在我们自动化过程中,能否构建一个健壮和可靠的测试是UI自动化测试能否成功的关键因素之一。然而在自动化过程中试着去执行的时候,常常会出现各种不同的状况,当使用脚本定位元素或者去验证程序的运行状态时,有时候会发现找不到元素,这可能是由于突然的资源受限或者网络延迟或者机器性能等各种因素引起的响应速度太慢导致的,这时候测试报告就会返回测试失败的结果。其实元素是正常加载的,只是加载的时间晚了一点,那么遇到这种情况我们该怎么解决呢?
我们需要在测试脚本中引入延时机制,来使脚本的运行速度与程序元素的加载速度匹配。通俗意义上讲,就是我们需要使脚本和程序的响应能够同步。
我们有三种等待机制,强制等待、隐式等待和显式等待。
- 如何使用隐式等待或显式等待?
- 在什么情况下使用隐式等待或显式等待?
- 什么情况下使用强制等待?
一.隐式等待
隐式等待是通过一定的时长等待页面上某元素加载完成。如果超出了设置的时长元素还没有加载完成,则会抛出NoSuchElementException的异常。
一旦设置,隐式等待时间就会作用于这个webdriver实例的整个周期或者一次完整测试的执行周期,并且webdriver会使其对所有测试步骤中包含整个界面的元素的查找时都有效,除非把默认时间设置回0。
webdriver提供了implicitly_wait()方法来实现隐式等待,默认参数是以秒为单位。如下实例:
from appium import webdriver
from selenium.common.exceptions import NoSuchElementException
import time
desired_caps = {
'platformName': 'Android',
'platformVersion': '6.0.1',
'deviceName':'127.0.0.1:21305',
'appPackage': 'com.baidu.homework',
'appActivity': 'com.baidu.homework.activity.index.IndexActivity',
'automationName': 'Appium',
'noReset': False,
# 输入中文参数配置
"unicodeKeyboard": True,
"resetKeyboard": True,
}
driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
driver.implicitly_wait(10)
def zuoyebang_login(un="zuoyebang", pw="zuoyebang!test"):
# 点击未登录按钮
driver.find_element_by_id("com.baidu.homework:id/rl_login_guide_layout").click()
# 点击密码登陆
driver.find_element_by_id("com.baidu.homework:id/sll_password").click()
#设置隐式等待时间10s
try:
# 输入用户名
print(time.strftime('%H:%M:%S', time.localtime(time.time())))
driver.find_element_by_id("com.baidu.homework:id/passport_phone_number_input_edit0").send_keys(un)
# 输入密码
driver.find_element_by_id("com.baidu.homework:id/passport_password_input_view").send_keys(pw)
except NoSuchElementException as e:
print(e)
print(time.strftime('%H:%M:%S', time.localtime(time.time())))
def zuoyebang_login2(un="zuoyebang", pw="zuoyebang!test"):
try:
# 输入密码
print(time.strftime('%H:%M:%S', time.localtime(time.time())))
driver.find_element_by_id("com.baidu.homework:id/passport_password_input_view1").send_keys(pw)
except NoSuchElementException as e:
print(e)
print(time.strftime('%H:%M:%S', time.localtime(time.time())))
if __name__ == '__main__':
# 调用登陆方法
zuoyebang_login()
zuoyebang_login2()
本例创建了两个登陆方法,并且设置的等待时间是10秒。首先这10秒并不是一个固定的时间,它并不会影响脚本的执行速度;其次,它并不是针对界面上的某一个元素进行等待,它是对整个全局元素进行等待。
当脚本执行到某一个元素定位时,如果元素能够定位到,则继续执行,如果元素定位不到,那么它就将以轮循的方式不断地去判断元素是否被定位到。假设元素在第3秒定位到了则继续执行,若直到10秒(超出设置时长)还没有定位到元素,那么就会抛出异常。
在上面的例子中,故意写错两个resource_id ,所以肯定定位不到元素,通过打印的时间可以看出,当执行输入用户名和密码时,超过了10秒的时间等待,就会理解抛出了异常。并且可以看出它是对全局元素都会进行等待。
Python Shell
14:28:31
Message: An element could not be located
on the page using the given search parameters.
14:28:41
14:28:41
Message: An element could not be located
on the page using the given search parameters.
14:28:51
隐式等待为webdriver中的完整的一个测试用例或者一组测试的同步,提供了通用的方法。它对于解决由于网络延迟或者动态加载元素所导致的程序响应时间不一致,是非常有效的。
二.显式等待
显式等待是appium中webdriver用于同步测试的另外一种等待机制。显式等待比隐式等待具备更好的操控性。 ,与隐式等待不同,我们可以为脚本设置一些预置或者定制化的条件,等待条件满足后再近些下一步测试。
在使用场景上,隐式等待用来做一个全局的控制,例如设置全局隐式等待6秒;显式等待可以只作用于仅有同步需求的测试用例,它可以只针对某一个元素进行等待。
webdriver提供了WebDriverWait类来实现显式等待,配合该类的until()和until_not()方法,在设置的时间内,默认每隔一段时间去检测一次当前页面下指定的元素是否加载完,加载完了就执行下一步,否则继续每隔一段时间去判断,指定时间截止,如果超时就会抛出异常(TimeoutException)。具体格式如下:
WebDriverWait(driver,timeout=10,poll_frequency=0.5,ignored_exceptions=None)
- driver:webdriver实例
- timeout:最长超时时间,默认以秒为单位
- poll_frequency:检测的时间间隔,默认为0.5秒
- ignored_exceptions:超时后的异常信息,默认情况下抛出TimeoutException异常
#调用该方法提供的驱动程序作为一个参数,直到返回值为True
until(method,message='')
#调用该方法提供的驱动程序作为一个参数,直到返回Fasle
until_not(method,message="")
如下所示:
WebDriverWait(driver,timeout=10,poll_frequency=0.5,\
ignored_exceptions=None).until(lambda x :x.find_element_by_id("resource_id"))
实例:
from appium import webdriver
from selenium.common.exceptions import NoSuchElementException, TimeoutException
import time
from selenium.webdriver.support.wait import WebDriverWait
desired_caps = {
'platformName': 'Android',
'platformVersion': '6.0.1',
'deviceName': '127.0.0.1:21305',
'appPackage': 'com.baidu.homework',
'appActivity': 'com.baidu.homework.activity.index.IndexActivity',
'automationName': 'Appium',
'noReset': False,
# 输入中文参数配置
"unicodeKeyboard": True,
"resetKeyboard": True,
}
driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
def zuoyebang_login(un="zuoyebang", pw="zuoyebang!test"):
# 点击未登录按钮
driver.find_element_by_id("com.baidu.homework:id/rl_login_guide_layout").click()
# 点击密码登陆
driver.find_element_by_id("com.baidu.homework:id/sll_password").click()
#设置隐式等待时间10s
try:
# 输入用户名
print(time.strftime('%H:%M:%S', time.localtime(time.time())))
us=WebDriverWait(driver, timeout=10, poll_frequency=0.5).until(lambda x: x.find_element_by_id("com.baidu.homework:id/passport_phone_number_input_edit0"),message='用户名输入框定位超时')
us.send_keys("un")
# 输入密码
driver.find_element_by_id("com.baidu.homework:id/passport_password_input_view").send_keys(pw)
except TimeoutException as e:
print(e)
print(time.strftime('%H:%M:%S', time.localtime(time.time())))
if __name__ == '__main__':
# 调用登陆方法
zuoyebang_login()
上面的实例是每隔0.5秒去判断一次元素是否已经定位到,一直等到10秒,如果超过10秒还没有定位到就会抛出异常。
执行结果如下:
Python Shell
15:48:22
Message: 用户名输入框定位超时
15:48:32
PS:应尽量避免在测试中隐式等待和显式等待混合使用来处理同步问题。相比隐式等待,显式等待能提供更好的可操控性。
三.强制等待
有时候我们希望脚本在执行到某一处位置时做固定时间的休眠,尤其是在脚本调试的过程中。这时候我可以使用time模块提供的sleep()方法。
from appium import webdriver
from selenium.common.exceptions import NoSuchElementException, TimeoutException
import time
from selenium.webdriver.support.wait import WebDriverWait
desired_caps = {
'platformName': 'Android',
'platformVersion': '6.0.1',
'deviceName': '127.0.0.1:21305',
'appPackage': 'com.baidu.homework',
'appActivity': 'com.baidu.homework.activity.index.IndexActivity',
'automationName': 'Appium',
'noReset': False,
# 输入中文参数配置
"unicodeKeyboard": True,
"resetKeyboard": True,
}
def zuoyebang_login(un="zuoyebang", pw="zuoyebang!test"):
# 点击未登录按钮
driver.find_element_by_id("com.baidu.homework:id/rl_login_guide_layout").click()
# 点击密码登陆
driver.find_element_by_id("com.baidu.homework:id/sll_password").click()
#设置隐式等待时间10s
try:
# 输入用户名
print(time.strftime('%H:%M:%S', time.localtime(time.time())))
time.sleep(10)
driver.find_element_by_id("com.baidu.homework:id/passport_phone_number_input_editd").send_keys("un")
# 输入密码
driver.find_element_by_id("com.baidu.homework:id/passport_password_input_view").send_keys(pw)
except NoSuchElementException as e:
print(e)
print(time.strftime('%H:%M:%S', time.localtime(time.time())))
if __name__ == '__main__':
# 调用登陆方法
zuoyebang_login()
在上面的实例中当执行到sleep()方法时会固定休眠10秒,然后再继续执行。sleep()方法默认参数是以秒为单位,如果设置的时长小于1秒,可以用小数表示,如sleep(0.6)表示休眠0.6秒。
Python Shell
16:02:17
Message: An element could not be located on the page using the given search parameters.
16:02:27