Selenium自動化常見問題

Selenium是最流行的web端自動化測試框架之一,用於自動執行用戶對被測產品的操作。Selenium是開源的,Selenium框架的核心組件是Selenium WebDriverSelenium WebDriver允許使用者在不同的瀏覽器(例如Chrome,Firefox,Internet Explorer,Microsoft Edge等)上執行測試用例。使用Selenium WebDriver的主要優點是它支持.NETJavaC#Python等。可以參考有關Selenium WebDriver體系結構的官方文檔以瞭解更多信息。

儘管Selenium簡化了Web網站或Web應用程序的測試,但測試開發人員在使用框架時面臨着許多Selenium自動化挑戰。讓我們看一下Selenium Automation中面臨的一些最常見挑戰及其比較不錯的解決方案。

誤報成功和誤報失敗

誤報成功也是測試結果成功的一種情況,即使實際情況並非如此。反之亦然,誤報失敗是測試失敗一種情況,即使一切都按預期進行,測試結果也會報告腳本執行過程中出現錯誤。誤報對自動化測試一直是最大的挑戰,當然Selenium也不例外。

當測試工程師通過Selenium腳本運行成百上千的測試用例時,可能會遇到一些不穩定的測試,這些測試顯示誤報。如果長時間不處理,可能會導致整個自動化測試項目失去價值,從而使測試人員的自動化測試腳本淪爲“廢物”。

測試腳本的穩定性無疑是Selenium自動化中最常見的挑戰之一。目前通用的解決辦法依然缺少,但從過往工作經驗來看,測試左移,獨立測試環境,統計腳本誤報率等等從流程上來解決這個難題是一個不錯的思路。

等待網頁加載JavaScript

現在很多網站包含需要JS異步加載Web元素,例如基於用戶選擇的下拉列表。則Selenium腳本在運行時可能會在這些Web元素時突然失效。發生這種情況是因爲WebDriver沒有處理網頁完全加載所花費的時間。爲了處理頁面加載的Selenium自動化中的異步加載的問題,需要使WebDriver等到該頁面的完整JavaScript加載完成之後再進行操作。在任何網頁上執行測試之前,您應確保該網頁(尤其是帶有很多JavaScript代碼的網頁)的加載已完成。您可以使用readyState屬性,該屬性描述文檔/網頁的加載狀態。document.readyState狀態爲complete表示頁面/文檔的解析已完成。

''' 在此示例中,我們將pytest框架與Selenium一起使用 '''
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
from time import sleep
from contextlib import contextmanager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support.expected_conditions import staleness_of

@pytest.fixture(params=["chrome"],scope="class")
def driver_init(request):
    if request.param == "chrome":
        web_driver = webdriver.Chrome()
    request.cls.driver = web_driver
    yield
    web_driver.close()

@pytest.mark.usefixtures("driver_init")
class BasicTest:
    pass
class Test_URL(BasicTest):
        def test_open_url(self):
            self.driver.get("https://www.*****.com/")
            print(self.driver.title)
            sleep(5)
  def wait_for_page_load(self, timeout=30):
            old_page = self.driver.find_element_by_id('owl-example')  
            yield
            WebDriverWait(self.driver, timeout).until(
                staleness_of(old_page)
            )
        
        def test_click_operation(self):
            # 等待10秒鐘的超時時間,然後執行CLICK操作
            with self.wait_for_page_load(timeout=10):
                self.driver.find_element_by_link_text('FREE SIGN UP').click()
                print(self.driver.execute_script("return document.readyState"))

無法擴展的方法

Selenium是一種出色的自動化測試工具,它是開源的,它使世界各地的Web測試人員的生活變得更加輕鬆。但是,Selenium自動化的主要挑戰之一是無法擴展。

執行自動化測試的最終目標是在更短的時間內覆蓋更多的測試範圍。最初可能會進行簡短的測試構建,但是產品勢必會隨着每個迭代版本發生變化。這意味着您可能需要涵蓋更多的測試用例。使用Selenium WebDriver,您只能以順序的方式執行測試,並且效果不如您希望的自動化過程有效,測試腳本的執行速度也會變得越來越慢。

現在,Selenium Grid可以並行運行測試用例,但這也有一個缺點。無法跨瀏覽器和操作系統的多種組合全面測試網站或網絡應用。因爲Selenium Grid僅有助於在本地計算機上安裝的特定瀏覽器上執行跨瀏覽器測試。您可以利用Selenium中的並行測試功能來代替線性測試,從而降低總體項目成本,並在並行執行自動化測試時加快產品/功能迭代交付。

處理動態內容

越來越多的網站使用了動態的內容。測試具有靜態內容的網站對Selenium自動化來說相對輕鬆。在當今時代,大多數網站所包含的內容可能因一個訪客而異。這意味着內容本質上是動態的(基於AJAX的應用程序)。

例如,電子商務網站可以根據用戶登錄的位置加載不同的產品、內容可能會有所不同,具體取決於用戶從特定下拉菜單中選擇的內容。由於新內容的加載需要時間,因此僅在加載完成後才觸發測試非常重要。由於網頁上的元素以不同的時間間隔加載,因此如果DOM中尚不存在某個元素,則可能會出現錯誤。這就是爲什麼處理動態內容一直是Selenium自動化中最常見的挑戰之一的原因。

解決此問題的一個簡單解決方案是使線程休眠幾秒鐘,這可能會提供足夠的時間來加載內容。但是,這不是一個好習慣,因爲,無論是否發生必需的事件,線程都會休眠那麼長的時間。

# 並不完美的方案
from selenium import webdriver
import time
from time import sleep
 
driver = webdriver.Firefox()
driver.get("https://www.*****.com")

# 睡眠10秒,無論是否存在元素
time.sleep(10)

# 資源釋放
driver.close()

使用Selenium WebDriver動態內容處理此挑戰的更好方法是使用隱式等待或顯式等待,這取決於各自的需求。

顯式等待處理動態內容

使用顯式等待,您可以使Selenium WebDriver停止執行並等待直到滿足特定條件。如果您希望設置條件以等待到確切的時間段,則可以將它與thread.sleep()函數一起使用。有多種方法可以實現顯式等待,帶有ExpectedConditionWebDriver是最受歡迎的選項。

from selenium import webdriver
from time import sleep
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait

driver = webdriver.Firefox()
driver.get("https://www.*****.com")

try:
    myElem_1 = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'home-btn')))
    print("Element 1 found")
    myElem_2 = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CLASS_NAME, 'login')))
    print("Element 2 found")
    myElem_2.click()
    sleep(10)
#異常處理
except TimeoutException:
    print("No element found")

sleep(10)

driver.close()

在上面的示例中,對受測URL內容進行了兩次搜索。第一次搜索是通過CLASS_NAME將元素定位到元素home-btn的最長持續時間爲10秒。

第二個搜索是可點擊元素登錄,最長持續時間爲10秒。如果存在clickable元素,則執行click()操作。在兩種情況下,都將WebDriverWaitExpectedCondition一起使用。WebDriverWait觸發ExpectedCondition的默認限制爲500毫秒,直到收到成功的響應。

隱式等待處理動態內容

隱式等待通知WebDriver在特定時間段內輪詢DOM,以獲取頁面上Web元素的存在。默認超時爲0秒。隱式等待需要一次性設置,如果配置正確,它將在Selenium WebDriver對象的生存週期內可用。

from selenium import webdriver
import time
from time import sleep
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.support import expected_conditions as EC
from builtins import str

driver = webdriver.Firefox()
driver.get("https://www.*****.com")
driver.implicitly_wait(60)
curr_time = time.time()
try: 
    curr_element = driver.find_element_by_class_name("home-btn") 
except: 
    print("元素不存在!")
print("耗時 " + str(int(time.time()-curr_time))+'-secs')

driver.close()

處理彈出窗口

在某些情況下,您需要與彈出窗口進行自動交互,這是Selenium自動化中最常見的挑戰之一。有不同類型的彈出窗口:

  • 簡單警報:顯示一些消息的東西。
  • 確認警報:要求用戶確認操作。
  • 提示警報:通知用戶輸入內容。

儘管Selenium WebDriver無法處理基於Windows的警報,但它確實具有處理基於Web的警報的功能。switch_to方法用於處理彈出窗口。爲了演示,我們創建了一個簡單的HTML文件,其中包含Alert彈出窗口實現。

<!DOCTYPE html>
<html>
<body>
<h2>
Demo for Alert</h3>


<button onclick="create_alert_dialogue()" name ="submit">Alert Creation</button>
<script>
function create_alert_dialogue() {
 alert("測試彈框,請點擊繼續!");
}
</script>
</body>
</html>

下面是我們在switch_to方法中的用法 .switch_to.alert.alert,以便切換到警報對話框。進入“警報框”後,可以使用alert.accept()方法接受警報。

from selenium import webdriver
import time
from time import sleep
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from builtins import str

driver = webdriver.Firefox()
driver.get("file://<HTML File location>")

driver.find_element_by_name("submit").click()
sleep(5)

alert = driver.switch_to.alert
text_in_alert = alert.text
sleep(10)

alert.accept()

print("ok,跳過警告框")

driver.close()

如果頁面顯示輸入警報,則可以使用send_keys()方法將文本發送到輸入框。

from selenium import webdriver
import time
from time import sleep
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from builtins import str

driver = webdriver.Firefox()
driver.get("file://<HTML File location>")

driver.find_element_by_name("***").send_keys("***")
sleep(5)

driver.find_element_by_name("submit").click()
sleep(5)

try:
    WebDriverWait(driver, 10).until(EC.alert_is_present(),
                '超時')
    alert = driver.switch_to.alert
    sleep(10)
    alert.accept()
    sleep(10)  
except TimeoutException:
    print("沒有發現警告框")

driver.close()

切換瀏覽器窗口

多窗口測試無疑是Selenium自動化中的常見挑戰之一。一種理想的情況是單擊按鈕會打開一個彈出窗口,該彈出窗口成爲子窗口。一旦有關子窗口上的活動的活動完成,則應將控件移交給父級或者上一級窗口。這可以通過使用switch_to.window()方法(其中window_handle作爲輸入參數傳遞)來實現。

用戶單擊鏈接後,將打開一個新的彈出窗口,該窗口將稱爲子窗口。switch_to.window方法用於切換回父窗口。可以使用driver.switch_to.window(window-handle-id)driver.switch_to.default_content()切換到父窗口。

import unittest
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from time import sleep
 
class test_switch_windows(unittest.TestCase):
 
    def setUp(self):
        self.driver = webdriver.Firefox()
    
    def test_open_pop_up_window(self):
        driver = self.driver
        driver.get("http://www.****.com/html/codes/html_popup_window_code.cfm")
        title1 = driver.title
        print(title1)
    

        #獲取當前窗口的窗口句柄
        parent_window = driver.window_handles[0]
        print(parent_window)
 
        #彈出式窗口是iframe
        driver.switch_to.frame(driver.find_element_by_name('result1'))
        driver.find_element_by_link_text('show_iframe').click()
        
        #獲取子窗口的句柄
        child_window = driver.window_handles[1]
        
        #父窗口將在後臺
        #子窗口來到前臺
        driver.switch_to.window(child_window)
        title2 = driver.title
        print(title2)
        print(child_window)
        
        #斷言主窗口和子窗口標題不匹配
        self.assertNotEqual(parent_window , child_window)
        
        sleep(5)
    
    def tearDown(self):
        self.driver.close()
 
if __name__ == "__main__":
    unittest.main()

無法測試移動設備

儘管Selenium框架已廣泛用於跨瀏覽器和操作系統的不同組合測試網站或Web應用程序,但該測試仍僅限於非移動設備。因此,測試針對移動設備的網站或Web應用程序是Selenium自動化面臨的重大挑戰之一。

如果對移動應用程序執行測試自動化測試,那麼最著名的開源框架將是Appium

無法自動化一切

100%自動化是一個吹牛的命題。衆所周知的是,並非所有測試方案都可以自動化,因爲有些測試需要手動干預。您需要確定團隊在自動化測試上相對於手動測試應花費的精力的優先級。儘管Selenium框架具有一些功能,可以通過這些功能來截取屏幕截圖,記錄視頻(測試的整體執行情況)以及可視化測試的其他方面,但是將這些功能與可擴展的基於雲的跨瀏覽器測試平臺一起使用可能會具有很大的價值。

生成測試報告

任何測試活動的主要目的是發現BUG並改善整體產品。在跟蹤正在執行的測試,生成的輸出和測試結果方面,報告可以發揮主要作用。儘管有可以與pytest和Selenium一起使用的模塊,例如pytest_html(對於Python),但是測試報告中的信息可能並不十分詳盡。Selenium可以使用不同類型的編程語言(例如JavaC#.Net等)的類似模塊/包,但是這些語言仍然存在相同的問題。收集測試報告是Selenium自動化中的關鍵挑戰之一。

很多基於Selenium的第三方雲測平臺,還有很多公司機遇Selenium開發的自己的報告框架提取,一般來說從以下幾個方面豐富報告信息:

  • 檢索構建信息,例如構建測試狀態,單個測試狀態,測試運行時間,錯誤和測試日誌
  • 通過命令獲取屏幕截圖
  • 瀏覽器環境的詳細信息

有所不能

上面提到的是Selenium自動化中的一些常見挑戰,就Selenium而言存在一些限制。只能使用Selenium框架來測試Web應用程序,即不能用於測試基於本地Windows的應用程序。在某些情況下可能需要使用這些場景,出於安全目的,因此自動化測試很難甚至永遠無法繞過一些嚴格身份驗證。


  • 公衆號FunTester首發,更多原創文章:FunTester440+原創文章,歡迎關注、交流,禁止第三方擅自轉載。

熱文精選

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章