2024/01/25
2024/04/02 (補充內容)
2025/02/14 (新增參考資料)
2025/03/25 (新增內容)
軟體測試 (吳濟聰老師碩士在職班敏捷式軟體開發課程教材)
利用Github Copilot
Build a Selenium Python Framework in Minutes with GitHub Copilot! (8:53)
Selenium with Copilot in Test Automation & Unit Testing (12:54)
根據軟體品質不同的面向,進行不同類型的測試
需求面
單元測試 (unit test):單一程式的測試
功能測試:模組或功能階層的測試
整合測試:系統整體的測試,有些公司會有不同層級的整合測試,將最後的測試稱為系統測試
探索性測試:除了已規劃的測試案例之外,進行未規劃的測試
單元測試、功能測試、整合測試通常都是事先規劃好的
使用者介面測試:使用者透過系統介面進行測試
可以透過selenium等工具進行測試
使用者介面單元測試
只測試使用者介面
端對端測試 (End-to-end test, E2E test)
測試使用者介面以及背後的邏輯
迴歸測試 (regression test):新增或更動功能時,測試過去已經測試且未被更動的功能
通常搭配自動化測試進行測試
穩定性
容錯測試:了解系統面對未預期錯誤的容錯程度
通常搭配自動化測試進行測試
安全性
安全性測試:進行資訊安全的相關測試
通常搭配自動化測試進行測試
效率
壓力測試:了解系統所能承受的壓力,以及在壓力下的表現
通常搭配自動化測試進行測試
可維護性
軟體的可維護性通常是過程的管理來達成,利用透過制定程式碼的規範(如: Clean Code原則)並進行人工的code review或自動化的code analysis工具。
Test Driven Development (by Martin Fowler)
簡單的說,測試驅動開發就是先準備好測試案例,甚至先寫好測試腳本再進行開發。很多人會認為,開發的時間都來不及了,怎麼可能先準備好測試案例? 以TDD的概念,就是在寫需求的時候,就開始準備測試案例,也利用撰寫測試案例的過程來確認需求,這樣並不會花費更多時間,因為可以節省誤會需求的浪費,另外,也會因為測試自動化,可以減少在測試上花的時間,也一併提昇開發的品質。因為Martin Flowler介紹TDD的時候,自動化測試的概念剛開始,是以單元測試為主,所以,一般為了區隔非單元測試的其他測試,就會使用Behavior Driven Development(BDD)或Acceptance Test-Driven Development(ATDD)。一般而言,由於BDD推薦使用Gherkins(Given When Then)來撰寫需求,所以,一般會把BDD與Gherkins畫上等號,而認為與ATDD在User Story中撰寫驗收條件不太一樣。所以,如果利用Gherkins(Given When Then)來撰寫User Story中的驗收條件,就是將兩個概念融合在一起了。
我們在這門課就利用User Story中的驗收條件,尤其是以利用Given、When、Then來撰寫的驗收條件。Given是指前提條件(也就是準備動作),When是所採取的動作,Then則是可驗證的結果。過去,BDD會使用Cucumber這樣的工具來進行測試。不過,也可以使用Selenium來進行自動化測試,只是不像Cucumber,在語法上直接看得到Given When Then。
行為導向開發 (Behavior Driven Development, BDD) (吳濟聰老師敏捷式軟體開發課程教材)
測試導向開發 (Test Driven Development, TDD) (吳濟聰老師敏捷式軟體開發課程教材)
Specification by Example (吳濟聰老師敏捷式軟體開發課程教材)
測試 (吳濟聰老師進階web程式設計課程教材)
首先,設定好測試環境:
為了使用python進行測試,我們需要安裝python
我們透過selenium來進行網頁測試,所以,也需要安裝python的selenium套件
我們可以安裝pytest來進行批次測試
可以利用vscode執行我們的測試程式,所以,也要安裝vscode及相關插件
安裝細節可參考:測試 (吳濟聰老師 進階Web程式設計課程教材)
所謂E2E測試,就是透過使用者介面去進行測試,一般的E2E測試算是一種黑箱測試,所謂的黑箱測試就是不需要去管系統開發語言或工具的測試方式。而selenium是個開源的自動化測試工具,很多的自動化測試工具都是基於selenium。
Selenium提供Web Driver來進行測試,Web Driver目前支援Java、Python、C#、Ruby、Javascript、Kotlin等常見的語言,以下我們利用Python來介紹如何透過Web Driver進行E2E測試。
根據Write your first Selenium script,selenium腳本有八個要素,但是一個腳本中不一定八個要素都存在,但主要就是由這八個元素組成:
Start the session
driver = webdriver.Chrome()
Take action on browser
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
Request browser information
title = driver.title
Establish Waiting Strategy
最簡單的等待策略是implicit wait,但是,有些元件需要久一點的時間,所以,建議使用比較複雜的等待策略
driver.implicitly_wait(0.5)
Find an element
text_box = driver.find_element(by=By.NAME, value="my-text")
submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")
Take action on element
text_box.send_keys("Selenium")
submit_button.click()
Request element information
text = message.text
End the session
driver.quit()
以下的完整python檔,是透過assert去進行驗證,而不是利用print進行人工判斷
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://www.selenium.dev/selenium/web/web-form.html")
title = driver.title
assert title == "Web form"
driver.implicitly_wait(0.5)
text_box = driver.find_element(by=By.NAME, value="my-text")
submit_button = driver.find_element(by=By.CSS_SELECTOR, value="button")
text_box.send_keys("Selenium")
submit_button.click()
message = driver.find_element(by=By.ID, value="message")
text = message.text
assert value == "Received!"
driver.quit()
假如我們的接受條件是:
Given 購買者想瀏覽所有產品
When 購買者點選瀏覽所有產品
Then 購買者可以看到所有產品的名稱、價格,若超過十個產品每頁十個產品
Given通常就是對應的準備動作或相依的測試程式,在這個接受條件中,並不需要任何準備動作或相依的測試程式
When則是對應的動作
Then則是對應的斷言(assertion)
利用以上的接受條件,請ChatGPT產生的pytest測試程式,pytest的寫法又跟陽春的python程式不太一樣。** 注意 ** 檔名前面要加「test_」
test_TestViewAllProducts.py
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
class TestViewAllProducts:
def setup_method(self, method):
self.driver = webdriver.Chrome() # 使用Chrome瀏覽器,請確保已安裝Chrome WebDriver
self.driver.get("https://example.com") # 替換成您的測試網站URL
def teardown_method(self, method):
self.driver.quit()
def test_view_all_products(self):
# 點擊瀏覽所有產品的連結,這裡假設連結的class為"view-all-products-link"
view_all_products_link = self.driver.find_element_by_class_name("view-all-products-link")
view_all_products_link.click()
# 等待產品頁面加載完成
WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "product-list")))
# 獲取所有產品的名稱和價格,假設每個產品元素的class分別為"product-name"和"product-price"
product_names = self.driver.find_elements_by_class_name("product-name")
product_prices = self.driver.find_elements_by_class_name("product-price")
# 打印產品名稱和價格
for name, price in zip(product_names, product_prices):
print("Product Name:", name.text)
print("Product Price:", price.text)
# 確認產品數量是否不超過十個
assert len(product_names) <= 10, "There are more than ten products in this category."
# 如果超過十個產品,確認是否有分頁功能
if len(product_names) > 10:
# 確認是否有下一頁按鈕,假設按鈕的class為"next-page-button"
next_page_button = self.driver.find_element_by_class_name("next-page-button")
assert next_page_button.is_displayed(), "Next page button is not displayed."
# 執行測試
if __name__ == "__main__":
test = TestViewAllProducts()
test.setup_method(None)
test.test_view_all_products()
test.teardown_method(None)
ChatGPT提醒有幾個部分需要處理:
"https://example.com": 替換為您的測試網站URL。
"view-all-products-link", "product-list", "product-name", "product-price", "next-page-button": 這些都是HTML元素的類名或其他屬性,請根據您的網站給出正確的類名或其他屬性。
首先,先更改測試網站URL。例如: http://localhost:3000/product,並檢查一下是否可取得網頁title。
self.driver.get("http://localhost:3000/product") # 替換成您的測試網站URL
assert self.driver.title, "Create Next App"
由於我們使用React撰寫程式,而且並沒有定義class,當然,也可以修改我們的React程式,加入id、class的定義。如果不去更動程式的話,可以利用XPath來取得元件。
# 等待產品頁面載入完成
WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "/html/body/div/div/div/ul")))
接下來,取得元件,ChatGPT使用的是舊的語法,新的語法是find_elements()
# 獲取所有產品
product_list = self.driver.find_elements(by=By.XPATH, value="/html/body/div/div/div/ul/li")
接下來算一下LI裡的元素一共有幾個
# 確認產品數量是否不超過十個
assert len(product_list) <= 10, "內容超過10筆"
接下來,取得第一筆資料,並且確認內容
# 獲取第一筆產品的名稱和價格,確認產品名稱與價格
product_name = self.driver.find_element(by=By.XPATH, value="/html/body/div/div/div/ul/li[1]/div/span")
# print (product_name.text)
product_price = self.driver.find_element(by=By.XPATH, value="/html/body/div/div/div/ul/li[1]/div/p")
# print (product_price.text)
assert product_name.text.startswith("這套神話故事教學繪本分為六本"),"內容錯誤"
assert int(product_price.text) == 260, "價格錯誤"
完整檔案:
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
class TestViewAllProducts:
def setup_method(self, method):
self.driver = webdriver.Chrome() # 使用Chrome瀏覽器,請確保已安裝Chrome WebDriver
self.driver.get("http://localhost:3000/product") # 替換成您的測試網站URL
assert self.driver.title, "Create Next App"
def teardown_method(self, method):
self.driver.quit()
def test_view_all_products(self):
# 等待產品頁面載入完成
WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, "/html/body/div/div/div/ul")))
# 獲取所有產品
product_list = self.driver.find_elements(by=By.XPATH, value="/html/body/div/div/div/ul/li")
# 確認產品數量是否不超過十個
assert len(product_list) <= 10, "內容超過10筆"
# 獲取第一筆產品的名稱和價格,確認產品名稱與價格
product_name = self.driver.find_element(by=By.XPATH, value="/html/body/div/div/div/ul/li[1]/div/span")
# print (product_name.text)
assert product_name.text.startswith("這套神話故事教學繪本分為六本"),"內容錯誤"
product_price = self.driver.find_element(by=By.XPATH, value="/html/body/div/div/div/ul/li[1]/div/p")
# print (product_price.text)
assert int(product_price.text) == 260, "價格錯誤"
# 執行測試
if __name__ == "__main__":
test = TestViewAllProducts()
test.setup_method(None)
test.test_view_all_products()
test.teardown_method(None)
葬送的軟體測試 - 不懂不想做是會出事 (2024 iThome 鐵人賽)
單元測試
整合測試
系統測試
驗收測試
Python 與自動化測試的敲門磚 (2022iThome鐵人賽)
開始系統測試 (2022iThome鐵人賽)
V模型
W模型
H模型
敏捷測試模型
網站自動化測試 for Javascript (2017)