綱要
不知道為什麼這篇莫名其妙在Google搜尋「Selenium 教學」的排名特別前面…
來信問開課的人突然暴增,但目前沒有收費開課的打算,所以之後會找個時間把進階教學寫一寫,有更多對E2E自動化測試有興趣的人一起學習是好事。
如果你已經使用了115版以後的版本,要改從這裡下載相應的Driver
https://googlechromelabs.github.io/chrome-for-testing/#stable
Selenium自動化測試需要借用能操作瀏覽器的Driver之外,也需要輔以一些額外的套件進行。
以下依照順序詳解:
安裝最新版本的Python(目前3.9.6):https://www.python.org/downloads/
安裝路徑預設在windows上是:
C:\Users\你的使用者名稱\AppData\Local\Programs\Python\Python39
記得在安裝畫面將Add Python 3.9 to PATH
也勾選起來,便於之後安裝一些Package。
安裝好後如下所示,以「系統管理者身分」執行cmd
接著在終端機上輸入python
,看到這訊息代表Python安裝成功了。
由於部分使用者在執行Python指令時,WINDOWS會彈出線上商店的關係。
現在需要到WINDOWS的系統環境變數修改些路徑:
按下「編輯系統環境變數」後會看到這個畫面,再按「環境變數」
進入環境變數視窗後選擇「path」>> 「編輯」,檢查看看Python的安裝路徑。
確認Python是不是擺在「windowApps」之前。
如果不是,就用右邊的上移、下移按鈕處理後按下確定。
假如你安裝Python時忘了把Add to path打勾
那就把這個路徑新增到系統變數 >> Path裡面去:
C:\Users\你的電腦帳號\AppData\Local\Programs\Python\Python目前版本\Scripts
權限問題排除
若敲指令時發生「python 不是内部或外部命令」的錯誤訊息出現時。
我們需要調整一下Windows的PowerShell權限設定。
在開始選單下的「Windows PowerShell」下,以系統管理員身分執行一次
並且輸入指令:Set-ExecutionPolicy RemoteSigned
輸入A選擇「全部皆是」,然後把VSCODE和終端機重開即可。
然後繼續輸入指令quit()
跳出剛才有>>>
的Python指令區
現在輸入我們要安裝Selenium的指令 pip install selenium
出現Successfully installed selenium
的訊息即可。
自動測試需要使用到瀏覽器行為的控制事件,所以我們要安裝相應的WebDriver以及版本。
如果你不清楚現在使用的Chrome版本,網址輸入chrome://settings/help
可以查看:
下載 Chrome WebDriver
https://chromedriver.chromium.org/downloads
如果有遇到Chrome更新,就要重新安裝對應版本的WebDriver
WebDriver存放的路徑,建議是放在Python安裝好的根目錄下,也就是:
C:\Users\你的使用者名稱\AppData\Local\Programs\Python\Python39
這樣即可在任何路徑下的腳本中隨時調用driver的功能。
第二個方式比較不推薦,不過當有著需要測試不同版本瀏覽器時可以參考看看:
regression_test
├── case_1
│ ├── chromedriver.exe
│ └── test_login.py
├── case_2
│ ├── chromedriver.exe
│ └── test_keno_bet.py
由於通常測試腳本都會進入Git版控管理,Driver本身的體積就有10MB左右
這樣做反而會讓專案打包Push上去遠端後佔用太多容量。
下載並安裝BeautifulReport
假如你已經會使用Git,直接Clone這個位置:
https://github.com/TesterlifeRaymond/BeautifulReport.git
~~或者前往Github網址: (作者不再更新,然後原始版本的cdn異常緩慢)
https://github.com/TesterlifeRaymond/BeautifulReport
建議試著用我改好cdn位置的繁體化修改版本:
https://drive.google.com/file/d/1zQlAt1p-0CQoXnIjJFLdFp7SCuuVkZG2/view?usp=sharing
按下Code後選擇Download ZIP
解壓縮後將目錄名稱改為BeautifulReport
點入BeautifulReport會看到這些檔案:
回上一層,將整個BeautifulReport目錄複製到以下路徑即可:
C:\Users\你的使用者名稱\AppData\Local\Programs\Python\Python39\Lib\site-packages\
安裝webdriver-manager
接著回到終端機,我們需要webdriver manager的支援。
輸入指令:
pip install webdriver-manager
網址:https://code.visualstudio.com/
安裝成功後打開VSCODE,按一下左下的Extensions開啟擴充套件視窗,搜尋以下幾個套件:
1. Pylance
微軟提供的語法智能輔助工具,包含語法檢查與Debugging。
2. Path Intellisense
路徑選擇的輔助工具,當需要輸入來源程式碼、圖片、CSS等檔案時
依照輸入 ../
或 ./
的不同,自動會幫我們開啟相關目錄下拉視窗來選擇來源檔案。
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install python3
python3 get-pip.py
確認有正確安裝pip 3
pip3 --version
pip3 install selenium
pip3 install webdriver-manager
https://chromedriver.chromium.org/downloads
下載好相應版本後放在mac根目錄下: /usr/local/bin
如果你的chrome版本超過114,請下載這裡的
https://googlechromelabs.github.io/chrome-for-testing/
下載位置:https://drive.google.com/file/d/1zQlAt1p-0CQoXnIjJFLdFp7SCuuVkZG2/view?usp=sharing
解壓縮到:
/opt/homebrew/lib/python3.11/site-package
和Windows沒有相異,所以VSCODE的設定參照一下Windows的部分即可
Python Selenium對於取得當前需要被操作的元素,常見方法有:
1. XPATH
HTML經過編譯成XML後找出的對應位置,分絕對位置與相對位置。
相對位置://div[@class='ytp-tooltip-text-wrapper']
絕對位置: /html[1]/body[1]/ytd-app[1]/div[1]/ytd-page-manager[1]/ytd-watch-flexy[1]/div[5]/div[1]/div[1]/div[1]/div[1]/div[1]/div[1]/ytd-player[1]/div[1]/div[1]/div[10]/div[1]
2. CSS Selector
可以透過CSS選取器取得元素定位。
3. ID
HTML上某元素指定的id="ooo"
4. ClassName
HTML上某元素含有的class="aaa bbb ccc"
先來安裝尋找XPATH的Chrome小工具: ChroPath
ChroPath
第二個工具是ChroPath,同樣加入擴充套件並將它釘選起來
https://chrome.google.com/webstore/detail/chropath/ljngjbnaijcbncmcnjfhigebomdlkcjo?hl=zh-TW
重新啟動瀏覽器後,在網頁上開啟開發人員工具(f12)
在Elements頁籤下,中間會有Styles、Computed等的選單,按下「>>」即可找到ChroPath
接著網頁上無論是透過開發人員工具或者滑鼠右鍵 >> 檢查,都可以找到該元素在ChroPath上的Rel XPATH(Relative相對位置)以及Abs XPATH(Absolute絕對位置)。
這能補足RUTO找不到完整XPATH時的缺陷
測試目標:打開Youtube搜尋hololive的虛擬主播節目
1. 建立新檔案
在D槽建立一個目錄,命名為auto_test
打開VSCODE,選取左上檔案圖示後點選Open Folder
在左側的檔案列表最上面,按下新增圖示,並將檔名命名為firstCase.py
2. 引入常用WebDriver API
from selenium import webdriver
## BY: 也就是依照條件尋找元素中XPATH、CLASS NAME、ID、CSS選擇器等都會用到的Library
from selenium.webdriver.common.by import By
## keys: 鍵盤相關的Library
from selenium.webdriver.common.keys import Keys
## Select: 下拉選單相關支援,但前端框架UI工具不適用(ex: Quasar、ElementUI、Bootstrap)
from selenium.webdriver.support.ui import Select
## WebDriverWait: 等待頁面加載完成的顯性等待機制Library
from selenium.webdriver.support.ui import WebDriverWait
## ActionChains: 滑鼠事件相關
from selenium.webdriver.common.action_chains import ActionChains
## expected_conditions: 條件相關
from selenium.webdriver.support import expected_conditions as EC
## BeautifulReport: 產生自動測試報告套件
from BeautifulReport import BeautifulReport
## Chrome WebDriver 需要DRIVER Manager的支援
from webdriver_manager.chrome import ChromeDriverManager
## 延遲時間相關
import time
## 單元測試模組,線性測試用不到
import unittest
ActionChain API 內容
點擊滑鼠右鍵: context_click()
雙擊滑鼠左鍵: double_click()
按著滑鼠左鍵不放: click_and_hold()
放開滑鼠左鍵: release()
拖曳到某個元素後放開: drag_and_drop(source, target)
拖曳到某個座標後放開: drag_and_drop_by_offset(source, xoffset, yoffset)
按下鍵盤上某個按鍵: key_down(value)
放開鍵盤上某個按鍵: key_up(value)
滑鼠指標從當前位置移動到某個座標: move_by_offset(xoffset, yoffset)
滑鼠指標移動到某個元素: move_to_element(to_element)
移動到某元素附近座標位置: move_to_element_with_offset(to_element, xoffset, yoffset)
執行當前這個ActionChain的動作: perform()
在元素上輸入值(ex:input): send_keys(value)
在指定的元素上輸入值: send_keys_to_element(element, value)
##剛才IMPORT的部分(省略)###
###
## 設定Chrome的瀏覽器彈出時遵照的規則
## 這串設定是防止瀏覽器上頭顯示「Chrome正受自動控制」
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
## 關閉自動記住密碼的提示彈窗
options.add_experimental_option("prefs", {
"profile.password_manager_enabled": False, "credentials_enable_service": False})
接下來是測試腳本的內容,我們要將腳本定義完成這些動作需要幾個Case。
## 剛才import進來的東西與基本設定,省略
####
## 我們如果要將CASE拆成幾個不同的方法,需要用一個Unitest Class包覆起來
## 然後加上修飾符@classmethod
class Test(unittest.TestCase):
@classmethod
## setUpClass這邊的設定是,可以讓所有Case進行過程中只開啟一次瀏覽器
## 執行時會依照這個順序循環一次 setUpClass > test > teardown
## self則是作為我們的區域參數來定義作用域
def setUpClass(self):
## 定義WebDriver以及ActionsChain的變數便於後頭應用
self.driver = webdriver.Chrome(chrome_options=options)
self.action = ActionChains(self.driver)
## 開啟Chrome新視窗,前往Youtube網址並最大化視窗
self.URL = "https://youtube.com"
self.driver.get(self.URL)
self.driver.maximize_window()
@classmethod
def tearDownClass(self):
## 所有case跑完後就退出瀏覽器
self.driver.quit()
## Test Case 的命名方式務必以「test_01_* ~ test_99_*」為主,讓爬蟲依照順序走
## """裡面的註解就是報表產生後的CASE描述文字。
## time.sleep(number)數字代表一秒
def test_01_search(self):
"""
前往Youtube網站後,搜尋Gura的影片
"""
time.sleep(2)
## 等待頁面中的HTML,ID = 'search'這個元素出現後才執行動作
try:
element = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//input[@id='search']"))
)
finally:
time.sleep(2)
## 找到搜尋框的XPATH並且輸入'Gawr Gura 熟肉' 熟肉就是經過別人得到版權方同意,翻譯上傳過後的剪輯
## 寫法上也通driver.find_element(By.XPATH, '//xxx')
input_search = self.driver.find_element_by_xpath("//input[@id='search']")
input_search.send_keys("Gawr Gura 熟肉")
## 找到搜尋按鈕後按下
button_search = self.driver.find_element_by_id("search-icon-legacy")
button_search.click()
def test_02_open_target(self):
"""
在搜尋結果找到特定的影片並點進去
"""
time.sleep(5)
## 滾動網頁往下拉
self.driver.execute_script("window.scrollBy(0,1200)")
time.sleep(2)
## 隨便找一個影片的連結,利用XPATH找路徑
youtube_target = self.driver.find_element_by_xpath("//a[@href='/watch?v=9SfsF_6fY9c']")
youtube_target.click()
time.sleep(5)
def test_03_back_to_list(self):
"""
回上頁列表,重新整理網頁後,切換另一則影片,五秒後回到首頁
"""
self.driver.back()
time.sleep(2)
self.driver.refresh()
time.sleep(3)
## 注意,youtube可能因為你所在位置而針對搜尋結果作排序
## 如果你發現突然跳出case導致失敗,很有可能是因為搜尋列表找不到影片
## 將影片的 /watch?v=xxxxx替換成該搜尋結果中的任一連結即可
youtube_target = self.driver.find_element_by_xpath("//a[@href='/watch?v=MhVh01k_Wg0']")
youtube_target.click()
time.sleep(5)
logo = self.driver.find_element_by_id("logo-icon")
logo.click()
time.sleep(5)
程式碼的尾端我們在這將產生測試報告的設定做好:
## 剛剛寫腳本的區域,省略
###
# basedir就是存放所有TEST Case的目錄,讓它爬 pattern = '*.py',所以要做哪個類別的測試就指定哪個前贅
# 路徑盡量放D槽喔,除非你的電腦只有C
basedir = "D:/auto_test/"
if __name__ == '__main__':
# 取得資料夾目錄底下,符合後面任何副檔名為.py,並進行所有test的測試項目
test_suite = unittest.defaultTestLoader.discover(
basedir, pattern='*.py')
# 測試結果加入到 BeautifulReport 套件內
result = BeautifulReport(test_suite)
# 結果產生Report 檔案名稱為 filename, 敘述為 description, log_path 預設放在跟目錄底下就行
result.report(filename='report',
description='我的第一個測試', log_path='D:/auto_test/')
# 啟動自動化指令,在終端機輸入: & C:/Users/你的使用者帳號/AppData/Local/Programs/Python/Python39/python.exe d:/auto_test/firstCase.py
跑自動測試的終端機啟動指令:
& C:/Users/你的使用者名稱/AppData/Local/Programs/Python/Python39/python.exe d:/auto_test/firstCase.py
執行自動化測試的這段時間你可以選擇從頭看到尾,也可以丟著讓他跑完等報告就好。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from BeautifulReport import BeautifulReport
from webdriver_manager.chrome import ChromeDriverManager
import time
import unittest
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
options.add_experimental_option("prefs", {
"profile.password_manager_enabled": False,
"credentials_enable_service": False
})
class Test(unittest.TestCase):
@classmethod
def setUpClass(self):
self.driver = webdriver.Chrome(chrome_options=options)
self.action = ActionChains(self.driver)
self.URL = "https://youtube.com"
self.driver.get(self.URL)
self.driver.maximize_window()
@classmethod
def tearDownClass(self):
self.driver.quit()
def test_01_search(self):
"""
前往Youtube網站後,搜尋Gura的影片
"""
time.sleep(2)
try:
element = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.XPATH, "//input[@id='search']"))
)
finally:
time.sleep(2)
input_search = self.driver.find_element_by_xpath("//input[@id='search']")
input_search.send_keys("Gawr Gura 熟肉")
button_search = self.driver.find_element_by_id("search-icon-legacy")
button_search.click()
def test_02_open_target(self):
"""
在搜尋結果找到特定的影片並點進去
"""
time.sleep(5)
self.driver.execute_script("window.scrollBy(0,1200)")
time.sleep(2)
youtube_target = self.driver.find_element_by_xpath("//a[@href='/watch?v=9SfsF_6fY9c']")
youtube_target.click()
time.sleep(5)
def test_03_back_to_list(self):
"""
回上頁列表,重新整理網頁後,切換另一則影片,五秒後回到首頁
"""
self.driver.back()
time.sleep(2)
self.driver.refresh()
time.sleep(3)
youtube_target = self.driver.find_element_by_xpath("//a[@href='/watch?v=MhVh01k_Wg0']")
youtube_target.click()
time.sleep(5)
logo = self.driver.find_element_by_id("logo-icon")
logo.click()
time.sleep(5)
basedir = "D:/auto_test/"
if __name__ == '__main__':
test_suite = unittest.defaultTestLoader.discover(basedir, pattern='*.py')
result = BeautifulReport(test_suite)
result.report(filename='report',description='我的第一個測試', log_path='D:/auto_test/')
如果你有檢查資料正確與否的需求
def 方法下面可以加上 assert 判別結果是否符合測試例子
def test_01_new_test(self):
"""
斷言例子
"""
test_val = "亂叫的小鯊魚"
## ...一些事件後
target_text = self.driver.find_element_by_xpath("任何你找到的位置").text
## assert 就是在驗證結果的布林值,沒事不建議直接讓他 = true/false
assert target_text = test_val
time.sleep(5)
順利的話報告會產生在D:/auto_test下,名稱是report.html
可以打開來看看,到這步驟就結束囉!
Python
Selenium