在进行 UI 自动化测试的时候,我们为了保持用例的稳定性,往往要设置显示等待,显示等待就是说明确的要等到某个元素的出现或者元素的某些条件出现,比如可点击、可见等条件,如果在规定的时间之内都没有找到,那么就会抛出Exception
.
上面是我用selenium
写的一个测试用例,展示了selenium
中显示等待的使用方式,其中会使用到expected_conditions
模块和WebDriverWait
类,注意这里expected_conditions
是一个 py 文件的文件名,也就是一个模块名,这个模块下面有很多的条件类,而我们用例中使用的title_is
就是一个条件类。
WebDriverWait
是一个类,这个类的作用就是根据一定的条件,不断的检查这个条件是否被满足了。WebDriverWait
类只有两个方法,一个是until
直到满足某个条件,另一个是until_not
直到不满足某个条件。
class WebDriverWait(object):
def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
WebDriverWait
有四个参数分别是,driver
驱动, timeout
超时时间, poll_frequency=POLL_FREQUENCY
轮训时间,也就是去判断条件是否满足的时间间隔,默认是 0.5 秒, ignored_exceptions=None
在等待的过程中需要忽略的异常,是一个可迭代的异常类集合,比如我们可以设置一个 list,里面是[NoSuchElementException,NoSuchAttributeException,InvalidElementStateException....]
,默认情况下,是一个元组,只包含一个NoSuchElementException
,因为只有元素出现,才能去判断条件是否满足,在不断轮训的过程中,肯定会发生NoSuchElementException
,这个时候必须忽略掉这个异常,不然程序就会中断。
其中driver
和timeout
是必传的位置参数,另外两个是选择传递的关键字参数,如果不传都有指定的默认值。
下面就进入我们今天的主题,selenium 中的等待条件的讨论
在selenium.webdriver.support.expected_conditions
这个模块里,存放着所有的等待条件,每个条件类的结构都是一样的一个__init__
构造方法和一个__call__
方法。
在 python 中,如果想把类型的对象当做函数来使用,那么就可以给这个类实现__call__
方法,如下:
class TestCase:
def __init__(self):
self.driver = webdriver.Chrome(executable_path="./driver/chromedriver")
self.driver.get('http://www.baidu.com')
# sleep(2)
def __call__(self):
print(self.driver.title)
if __name__ == '__main__':
case = TestCase()
case()
case()
对象的调用,就会执行__call__
方法里面的逻辑打印当前页面的标题,我们取一个 selenium 的实现类:
class presence_of_element_located(object):
def __init__(self, locator):
self.locator = locator
def __call__(self, driver):
return _find_element(driver, self.locator)
这个条件类的意思是判断一个元素是否已经渲染到页面当中,在使用这个条件的时候需要先实例化,传入元素的定位,然后要进行判断的时候需要对实例对象进行调用并传入driver
,对实例对象进行调用的时候就会执行__call__
方法里的条件判断逻辑。
WebDriverWait
是如何进行条件判断的再回到文章开头看一下我们使用显示等待的代码:
wait = WebDriverWait(self.driver, 2)
wait.until(EC.title_is('百度一下,你就知道'))
先是实例化一个WebDriverWait
对象,然后再调用until
方法并且传递一个条件的实例对象,until
方法里就会不断的去轮训条件是否满足。
def until(self, method, message=''):
screen = None
stacktrace = None
end_time = time.time() + self._timeout
while True:
try:
value = method(self._driver)
if value:
return value
except self._ignored_exceptions as exc:
screen = getattr(exc, 'screen', None)
stacktrace = getattr(exc, 'stacktrace', None)
time.sleep(self._poll)
if time.time() > end_time:
break
raise TimeoutException(message, screen, stacktrace)
method
这个参数就是我们传递进来的条件的实例对象,value = method(self._driver)
这里就是进行对象的调用,也就是执行了__call__
方法里的逻辑。
以上就是 selenium 支持的所有条件。
说了那么多条件,其实我们也可以自己实现一个条件类,
class page_is_load:
def __init__(self, expected_title, expected_url):
self.expected_title = expected_title
self.expected_url = expected_url
def __call__(self, driver):
is_title_correct = driver.title == self.expected_title
is_url_correct = driver.current_url == self.expected_url
return is_title_correct and is_url_correct
上面是自己实现的一个条件类,根据页面的 url 和标题来判断页面是否被正确加载,
class TestCase:
def __init__(self):
self.driver = webdriver.Chrome(executable_path="./driver/chromedriver")
self.driver.get('http://www.baidu.com/')
# sleep(2)
def __call__(self):
print(self.driver.title)
def test_wait(self):
wait = WebDriverWait(self.driver, 2)
wait.until(page_is_load("百度一下,你就知道", "http://www.baidu.com/"))
欢迎大家去 我的博客 瞅瞅