作者简介:博主是一位测试管理者,同时也是一名对外企业兼职讲师。
主页地址:【Austin_zhai】
目的与景愿:旨在于能帮助更多的测试行业人员提升软硬技能,分享行业相关最新信息。
声明:博主日常工作较为繁忙,文章会不定期更新,各类行业或职场问题欢迎大家私信,有空必回。
我们在日常的web ui测试工作中经常会碰到页面中存在动态内容与通过Ajax异步加载的元素内容,针对这些非静态元素我们的自动化测试代码就需要进行一些对应的处理,才能确保元素可以被正确的加载与捕捉,那么今天我们就围绕着这一话题来说说如何在自动化测试中对异步通信与动态内容进行处理。
说到前后端的异步通信,在web框架中想来最最有名的应该非Ajax莫属了。Ajax (Asynchronous JavaScript and XML),简单来说就是通过JavaScript和XMLHttpRequest对象,可以在不重新加载整个页面的情况下,通过后台请求获取数据并更新页面内容的一种技术。
那么针对页面中的Ajaxy异步通信,显然我们使用传统的页面加载代码是肯定无法满足业务场景的需求,那么我们就可以利用selenium中的显式等待方法来针对异步通信加载元素的要求。
selenium中的显式等待相信大家一定也不陌生了,它的特性就是等待特定的元素加载完成,那么我们就可以利用这一特性,来等待异步通信加载的元素。最常见的就是WebDriverWait
和 expected_conditions
的组合了。
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 等待元素出现
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'element_id')))
# 等待元素可点击
element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, 'element_id')))
当然光光简单的显示等待只能解决部分的测试场景,其实在异步通信的情况下来说,我们还有很多的场景需要处理,比如:当页面中的 Ajax 请求完成后,可能会有特定的元素或特定的页面状态发生变化、Ajax 请求完成后,页面元素的某些属性会发生变化,例如文本内容、class 属性等。所以我们不能只是很死板的使用显式等待,在业务代码的逻辑中往往需要加入更多的场景处理方法,针对上面所说的一些情况,加入一些逻辑判断与特定操作是很有必要的,根据异步的特性,我们大可以在显式等待加载完元素之后对产生变化的部分就行判断,从而来检查Ajax的请求是否已经被完成,包括一些元素的属性。更甚至可以使用JS来等待Ajax请求完成后,再进行对应的业务操作。
# 等待 Ajax 请求完成的示例
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
def wait_for_ajax(driver):
wait = WebDriverWait(driver, 10)
wait.until(lambda driver: driver.execute_script("return jQuery.active == 0"))
# 使用示例
driver.get("https://www.testxxxx.com")
# 执行触发 Ajax 请求的操作
# ...
# 等待 Ajax 请求完成
wait_for_ajax(driver)
# 继续执行其他操作
接下来针对Ajax异步通信,我们来看一个简单的例子,博主把一个金融系统的业务测试代码简化之后,就有了一下这一段。在这个测试场景中,账户交易历史页面会使用 Ajax 异步加载最近的交易记录,并使用动态更新显示账户余额。这里我们使用显式等待来等待 Ajax 请求的完成,获取交易记录,然后根据交易记录计算账户的可用余额。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def get_recent_transactions_and_balance(driver):
driver.get("https://test-bank.com/transaction-history")
# 交易记录列表的ID为 "transaction_list"
wait = WebDriverWait(driver, 10)
transaction_list = wait.until(EC.presence_of_element_located((By.ID, "transaction_list")))
# 获取最近的交易记录
transactions = transaction_list.find_elements(By.XPATH, "//li[contains(@class, 'transaction-item')]")
recent_transactions = [transaction.text for transaction in transactions]
# 测试账户余额的元素ID为 "account_balance"
account_balance_element = wait.until(EC.presence_of_element_located((By.ID, "account_balance")))
# 获取账户余额并返回
account_balance = account_balance_element.text
return recent_transactions, account_balance
if __name__ == "__main__":
driver = webdriver.Chrome()
try:
recent_transactions, account_balance = get_recent_transactions_and_balance(driver)
print("最近的交易记录:", recent_transactions)
print("账户余额为:", account_balance)
finally:
driver.quit()
动态内容与前面说的Ajax异步通信又有所不同,通俗的来说,动态内容不单单可以通过Ajax请求去获得,也可以是用户在页面中进行业务操作交互后产生的新的数据。通常这些操作都是通过JS等技术进行生成与更新而产生的。
而针对动态内容的元素加载,这里仍然是使用基本的显式等待,具体的方法与Ajax部分的一致,就不展开重复解释了。这里需要重点说明的是在动态内容产生后该如何正确的捕获到对应的具体元素。动态元素的定位我们一般不使用find_element
方法,而是使用find_elements
,具体请看如下的例子:
# 获取动态元素列表
elements = driver.find_elements(By.XPATH, "//li[contains(@class, 'dynamic-element')]")
# 遍历动态元素列表,找到需要的元素
for element in elements:
if element.text == "target_text":
# 执行操作
element.click()
使用find_elements
是因为我们需要获取对应的元素列表,再根据具体条件找到需要的元素。这样可以在元素变化时重新获取元素,避免由于旧元素引起的问题。
同样的,我们也可以使用JS脚本来判断页面中特定元素的属性变化,或使用页面状态来判断动态内容是否已加载完成。
# 使用 JavaScript 判断特定属性的变化
element = driver.find_element(By.ID, "dynamic_element")
if driver.execute_script("return arguments[0].getAttribute('class');", element) == "new_class":
# 执行操作
# 使用页面状态判断动态内容是否加载完成
wait = WebDriverWait(driver, 10)
wait.until(lambda driver: driver.execute_script("return document.readyState") == "complete")
这里还是需要提醒大家的是,虽然我们可以利用以上的这些方法来进行元素加载与变化后的判断,但在日常的业务测试中,可能实际情况并没有我们想象的那么乐观,所以在我们的测试业务代码中,还是需要加入一些保障机制,而多重验证与异常处理则是此类情况下性价比较高的一些方案,在不同的节点多次查找指定的元素或在可能出现问题的场景设置一些异常处理与具体错误抛出,都是可以大大提高我们代码健壮性的重要因素。
这里我们来据另外一个场景,假设我们的投资交易的过程可能涉及到动态内容的变化,例如投资金额、预计收益等信息会在投资操作后动态更新在页面上。我们依然可以使用显式等待来等待动态内容的出现或更新,并获取投资结果。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def perform_investment(driver, amount):
driver.get("https://test-investment.com/trade")
# 投资金额输入框的元素ID为 "investment_amount"
investment_amount_input = driver.find_element(By.ID, "investment_amount")
investment_amount_input.send_keys(amount)
# 投资按钮的元素ID为 "invest_button"
invest_button = driver.find_element(By.ID, "invest_button")
invest_button.click()
# 使用显式等待等待投资结果信息的出现或更新
wait = WebDriverWait(driver, 10)
investment_result = wait.until(EC.presence_of_element_located((By.ID, "investment_result")))
# 获取投资结果信息并返回
result_text = investment_result.text
return result_text
if __name__ == "__main__":
driver = webdriver.Chrome()
try:
investment_amount = 5000
result = perform_investment(driver, investment_amount)
print("投资结果:", result)
finally:
driver.quit()
当然,在实际测试工作中,除了上面说的这些内容之外,其实还有很多很多的前后端技术可以来控制页面中的元素动态产生与显式,这里展示的处理方法只是最基础的方式,更多的复杂场景与高效处理方式还需要大家在工作中有意识的多多踩坑与积累。