在先前的 Pytest 篇章中,我們提到過
自動化測試能夠快速重複執行大量測試,開發者可以利用它來驗證舊有功能,確保新更改不會破壞現有功能,從而有效解決之前提到的問題。
在該範例中,我們測試了兩個函式:get_expensive_product_names
和 get_most_expensive_product_name
。這些測試屬於單元測試(Unit Testing),其目的是專注於應用程序中的單個組件或模組(如函式或類別),並驗證其正確性。
本篇將介紹單元測試的另一個重要對應面——端對端測試 (End-to-End Testing, 簡稱 E2E 測試)。端對端測試模擬真實用戶的操作,檢查整個應用程序從開始到結束的功能運作,確保所有組件和系統協同合作,並能夠按預期運行。
對於網頁應用程序,使用者通常會通過瀏覽器與系統交互,這包括瀏覽資訊、點擊選單和按鈕等操作。在進行端對端測試時,我們需要模擬這些交互行為,從而驗證應用程序的整體用戶體驗是否無誤。
在 Python 環境中,最廣泛使用的端對端測試工具之一是 Selenium。Selenium 提供了多種方法來查找和定位 HTML DOM 元素(如 <p>
、<div>
等),並且支持多個主流瀏覽器,包括 Chrome、Firefox、Safari 和 Edge 等,使開發者能夠在不同環境中進行跨瀏覽器測試。
本次範例使用的是 Selenium 2.3.3 版本
poetry add selenium==4.25.0
由於 Selenium 會使用真實的瀏覽器來模擬使用者的操作,因此我們需要先安裝對應的 Web Driver。Web Driver 是連接 Selenium 和瀏覽器的橋樑,它負責控制瀏覽器執行如點擊、輸入、滾動等操作。Selenium 官方網站上提供了多個常見瀏覽器的 Web Driver,包含 Chrome、Edge、Firefox 等主流瀏覽器。開發者可以根據自己的需求,下載並安裝對應的 Web Driver。
在本次範例中,我們將以 Chrome 瀏覽器為例進行演示,因此需要安裝 ChromeDriver。ChromeDriver 是專門用來控制 Chrome 瀏覽器的 Web Driver,通過它,我們可以利用 Selenium 自動化測試 Chrome 瀏覽器上的各種操作。
針對 Product Code 的部分,我們將實作一個基本的網頁應用程式,使用者可以透過瀏覽器發出請求,並與網頁互動,系統會將結果即時呈現在瀏覽器上。同時,使用者還可以直接與 HTML DOM 進行交互操作。
首先,我們透過以下指令創建專案目錄:
poetry new demo_selenium
創建專案後,進入專案目錄,並建立一個名為 index.html
的基本 HTML 檔案。在這個網頁中,當使用者點擊按鈕時,將會動態新增一段文字 "Hello World" 到頁面上。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask Example</title>
<script>
function insertText() {
const div = document.getElementById("dynamic-text");
div.innerHTML = "<p>Hello World</p>";
}
</script>
</head>
<body>
<h1>Flask Example</h1>
<button id="my-button" onclick="insertText()">Click Me!</button>
<div id="dynamic-text"></div>
</body>
</html>
接下來,在專案目錄下運行以下命令,啟動簡單的本地伺服器:
poetry run python -m http.server 8000
啟動伺服器後,使用瀏覽器訪問 http://localhost:8000/index.html
,即可以載入我們的網頁。點擊頁面中的按鈕後,你將會看到動態呈現的 "Hello World" 字串。這段操作流程正是我們接下來將要模擬和測試的情境,透過自動化測試工具來驗證按鈕與頁面交互的正確性。
此次的 Test Code 同樣使用 pytest 作為測試框架。我們首先需要在 /demo_selenium/tests
目錄下建立一個名為 test_selenium_index.py
的檔案,並在其中設置一個 browser
Fixture。該變數將用於模擬使用者操作瀏覽器的過程。
接下來,我們將測試流程分為四個部分:
輸入網址
在測試的第一步,使用者會透過瀏覽器輸入網址來取得網頁。本次測試的網址是先前已成功架設的 localhost:8000/index.html
,這是我們的本地伺服器提供的網頁。瀏覽器會在此頁面上進行後續的測試操作。
等待按鈕出現並點擊
由於網頁在載入或進行變動時通常需要一段時間,我們無法假設 DOM 元件會立即出現。因此,在 Selenium 中,我們會使用以下語法進行等待操作:
WebDriverWait(browser, 10).until(...)
這段代碼告訴瀏覽器等待最多 10 秒,直到某個 DOM 元件(在本例中是 id 為 my-button
的按鈕)出現。如果超過 10 秒仍未找到該元素,測試將失敗。一旦按鈕成功載入,我們會使用 .click()
來模擬使用者的點擊操作。
dynamic-text
的容器出現,接著我們期待該容器內部會生成一個 <p>
元素來顯示文字。在等待 p 元素出現的過程中,我們使用了一個特定的 XPath 表達式。XPath 是一種用於查詢 XML 文檔的語言,然而,它在 HTML 文檔中同樣適用,特別是在網頁自動化測試(如 Selenium)和數據提取的場合。這讓我們能夠準確地定位頁面中的元素。
Act
階段,即模擬使用者操作。接下來進行的是 Assert
,也就是驗證步驟。我們將檢查動態新增的文字內容是否等於預期的 "Hello World。import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
@pytest.fixture
def browser():
browser = webdriver.Chrome() # 或者使用其他瀏覽器
yield browser
browser.quit()
def test_button_click(browser):
# Act
browser.get("http://localhost:8000/index.html")
# 等待 ID 為 my-button 的按鈕出現
my_button = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.ID, "my-button"))
)
my_button.click()
# 等待 <div id="dynamic-text"> 出現
dynamic_text_div = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.ID, "dynamic-text"))
)
# 在確保 <div> 存在後,等待 <p> 出現
p_element = WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.XPATH, ".//div[@id='dynamic-text']/p"))
)
# Assert
assert p_element.text == "Hello World"
最後我們回到專案的根目錄,並執行poetry run pytest
即可以看測試通過。透過自動化測試,我們能夠快速檢查應用程式的功能是否如預期運行,確保頁面交互的正確性。
許多人在介紹 Selenium 時,常會提到其在網頁爬蟲中的應用,利用 Selenium 爬取他人網站的資訊。然而,這樣的做法其實不太好,因為它可能對他人的網站造成額外的負擔,並可能觸犯法律和道德規範。因此,在進行任何爬取操作之前,建議先仔細查閱目標網站的使用條款和規範,以確保遵守相關規定,並尊重網站擁有者的權益。