寫在最前面:目前自動化測試並不屬於新鮮的事物,或者說自動化測試的各類方法論已經層出不窮,可是,可以在項目中鍥而不捨的實踐自動化測試的團隊,卻依舊不是很是多。有的團隊知道怎麼作,作的還不夠好;有的團隊還正在探索和摸索怎麼作,甚至還有一些多方面的技術上和非技術上的舊系統須要重構……css
本文將會從
使用
和實踐
兩個視角,嘗試對基於Web UI
自動化測試作細緻的分析和解讀,給各位去思考和實踐作一點引路,以便各團隊能找到更好的方式。html
《論語》有云:工欲善其事,必先利其器。在開始具體的自動化測試以前,咱們須要作好更多的準備,包括如下幾個方面:前端
認識自動化測試node
準備自動化測試工具python
使用有效的方式mysql
針對具體的測試對象git
接下來的第一部份內容,咱們將會從上述的幾個方面進行探討。github
自動化測試的5W
web
正如開篇所提到的,自動化測試再也不是一個陌生的話題,而是一個具體的存在。做爲測試實踐活動的一部分,咱們首先分析一下自動化測試的方方面面。sql
WHAT
, 什麼是自動化測試
G.J.Myers在其經典的著做《軟件測試藝術》(The Art of Software Testing)一書中,給出了測試的定義:
「程序測試是爲了發現錯誤而執行的過程。」
這個概念產生於30年前,對軟件測試的認識還很是有侷限性,固然也是由於受瀑布開發模型的影響,認爲軟件測試是編程以後的一個階段。只有等待代碼開發出來之後,經過執行程序,像用戶那樣操做軟件去發現問題。這裏向你們推薦一個測試交流圈q裙:1007119548。
自動化測試:以人爲驅動的測試行爲轉化爲機器執行的一種過程
自動化測試,就是把手工進行的測試過程,轉變成機器自動執行的測試過程。該過程,依舊是爲了發現錯誤而執行。所以自動化測試的關鍵在於「自動化」三個字。自動化測試的內容,也就相應的轉變成如何「自動化」去實現本來手工進行的測試的過程。
全部的「自動化」,依靠的無疑都是程序。
經過程序,能夠把手工測試,轉變成自動化測試。
WHEN
, 在何時開展自動化測試
自動化測試的開展,依賴於「程序」。那麼程序,其實就是由「源代碼」構建而來的。那麼原則上,只要能作出自動化測試所須要的「程序」的時候,變能夠進行自動化測試。但每每,並非全部的「時候」都是好的「時機」。從這個W
開始,咱們將會加入對於成本的顧慮,也正是由於「成本」的存在,才使得下面的討論,變得有意義。
全部的開銷,都是有成本的。構建成「程序」的源代碼,也是由工程師寫出來的。那麼須要考慮這個過程當中的成本。基於這個考慮,在可以比較穩定的構建「程序」的時候,不須要花費太多開銷在「源代碼」的時候,就是開展自動化測試的好時機。這個開銷包括編寫
和修改
源代碼,而源代碼指的是構建出用來作自動化測試的程序
的源代碼。
WHERE
, 在什麼地方進行自動化測試
自動化測試的執行,依靠的是機器。那麼自動化測試必將在「機器」上進行。通常來講,這個機器包括桌面電腦和服務器。經過將寫好的源代碼
部署在機器上,構建出用來作自動化測試的"程序",而且運行該程序,實現自動化測試。
WHICH
, 對什麼目標進行自動化測試
自動化測試的目標,是被測試的軟件。拋開人工智能的成分,手工測試必將在「人工智能」足夠普及和足夠「智能」以前,替代一大部分不須要「人類智能」的手工測試;以及自動化測試會作一些手工測試沒法實施的,或者手工測試沒法覆蓋的測試。
HOW
, 如何開展自動化測試
和全部的其餘測試同樣,自動化測試的流程也是由「用例」執行和「缺陷」驗證組成。差異是須要找到合適的「工具」來替代「人手」。不一樣目標的自動化測試有不一樣的測試工具,可是任何工具都無不例外的須要「編程」的過程,實現「源代碼」,也能夠稱之爲測試腳本。因而開展自動化測試的方式基本上以下:
自動化測試的典型金字塔原理
談到自動化測試,就不得不提的一我的和概念就是:Martin Fowler和他的金字塔原理。首先請看金字塔原理的圖示以下:
該圖說明了三個問題:
這是理想中的金字塔原理。
在實際的項目中,尤爲是結合國內的項目實踐,其實還隱藏了另外一個問題:越是頂層的測試,效果越明顯。有句話說「貴的東西,除了貴,其餘都是好的!」可以很清晰的闡述這個觀點。
金字塔原理在國內的適應性也有必定的問題
相對來講,在基於UI前端界面的自動化測試反卻是開展和實施的不是特別多。儘管基於界面的測試帶來的效果仍是可以立竿見影的。對於產品的質量提高,仍是比較容易有保證。
自動化測試的適用範圍
自動化測試能夠涉及和試用的範圍主要在如下方面:
Web UI
的瀏覽器應用的界面測試WebService
或者WebAPI
的服務契約測試WCF
、.net remoting
、Spring
等框架的服務的集成測試APP UI
的移動應用界面測試Java
、C#
等編程文件進行的單元測試本文集中討論第一條:基於Web UI
的瀏覽器應用的界面測試。界面的改動對於測試來講,具備較大的成本風險。主要考慮如下方面:
自動化測試的流程
自動化測試和普通的手工測試遵循的測試流程,與項目的具體實踐相關。通常來講,也是須要從測試計劃開始涉及自動化測試的。
基於Web UI
的自動化測試工具主要有兩大類:付費的商業版工具和無償使用的開源版工具。典型的有兩種:
1.2.1 Selenium 基本介紹
Selenium`是開源的自動化測試工具,它主要是用於Web 應用程序的自動化測試,不僅侷限於此,同時支持全部基於web 的管理任務自動化。
Selenium
官網的介紹
Selenium is a suite of tools to automate web browsers across many platforms.
- runs in many browsers and operating systems
- can be controlled by many programming languages and testing frameworks.
Selenium 是用於測試 Web 應用程序用戶界面 (UI) 的經常使用框架。它是一款用於運行端到端功能測試的超強工具。您可使用多個編程語言編寫測試,而且 Selenium 可以在一個或多個瀏覽器中執行這些測試。
Selenium 經歷了三個版本:Selenium 1,Selenium 2 和 Selenium 3。Selenium 也不是簡單一個工具,而是由幾個工具組成,每一個工具都有其特色和應用場景。
Selenium 誕生於 2004 年,當在 ThoughtWorks
工做的 Jason Huggins 在測試一個內部應用時。做爲一個聰明的傢伙,他意識到相對於每次改動都須要手工進行測試,他的時間應該用得更有價值。他開發了一個能夠驅動頁面進行交互的 Javascript 庫,能讓多瀏覽器自動返回測試結果。那個庫最終變成了 Selenium 的核心,它是 Selenium RC(遠程控制)和 Selenium IDE 全部功能的基礎。Selenium RC 是開拓性的,由於沒有其餘產品能讓你使用本身喜歡的語言來控制瀏覽器。這就是 Selenium 1。
然而,因爲它使用了基於 Javascript 的自動化引擎,而瀏覽器對 Javascript 又有不少安全限制,有些事情就難以實現。更糟糕的是,網站應用正變得愈來愈強大,它們使用了新瀏覽器提供的各類特性,都使得這些限制讓人痛苦不堪。
在 2006 年,一名 Google 的工程師, Simon Stewart 開始基於這個項目進行開發,這個項目被命名爲 WebDriver。此時,Google 早已經是 Selenium 的重度用戶,可是測試工程師們不得不繞過它的限制進行工具。Simon 須要一款能經過瀏覽器和操做系統的本地方法直接和瀏覽器進行通話的測試工具,來解決Javascript 環境沙箱的問題。WebDriver 項目的目標就是要解決 Selenium 的痛點。
到了 2008 年,Selenium 和 WebDriver 兩個項目合併。Selenium 有着豐富的社區和商業支持,但 WebDriver 顯然表明着將來的趨勢。二者的合併爲全部用戶提供了一組通用功能,而且借鑑了一些測試自動化領域最閃光的思想。這就是 Selenium 2。
2016 年,Selenium 3 誕生。移除了再也不使用的 Selenium 1 中的 Selenium RC,而且官方重寫了全部的瀏覽器驅動。
Selenium 工具集
Selenium IDE
Selenium IDE (集成開發環境) 是一個建立測試腳本的原型工具。它是一個 Firefox 插件,實現簡單的瀏覽器操做的錄製與回放功能,提供建立自動化測試的建議接口。Selenium IDE 有一個記錄功能,能記錄用戶的操做,而且能選擇多種語言把它們導出到一個可重用的腳本中用於後續執行。這裏向你們推薦一個測試交流圈q裙:1007119548。
Selenium RC
Selenium RC 是selenium 家族的核心工具,Selenium RC 支持多種不一樣的語言編寫自動化測試腳本,經過selenium RC 的服務器做爲代理服務器去訪問應用從而達到測試的目的。
selenium RC 使用分Client Libraries 和Selenium Server。
Selenium Grid
Selenium Grid 使得 Selenium RC 解決方案能提高針對大型的測試套件或者哪些須要運行在多環境的測試套件的處理能力。Selenium Grid 能讓你並行的運行你的測試,也就是說,不一樣的測試能夠同時跑在不一樣的遠程機器上。這樣作有兩個有事,首先,若是你有一個大型的測試套件,或者一個跑的很慢的測試套件,你可使用 Selenium Grid 將你的測試套件劃分紅幾份同時在幾個不一樣的機器上運行,這樣能顯著的提高它的性能。同時,若是你必須在多環境中運行你的測試套件,你能夠得到多個遠程機器的支持,它們將同時運行你的測試套件。在每種狀況下,Selenium Grid 都能經過並行處理顯著地縮短你的測試套件的處理時間。
Selenium WebDriver
WebDriver 是 Selenium 2 主推的工具,事實上WebDriver是Selenium RC的替代品,由於Selenium須要保留向下兼容性的緣由,在 Selenium 2 中, Selenium RC纔沒有被完全的拋棄,若是使用Selenium開發一個新的自動化測試項目,那麼咱們強烈推薦使用Selenium2 的 WebDriver進行編碼。另外, 在Selenium 3 中,Selenium RC 被移除了。
Python 語言的選擇,便捷
/'paɪθən/
使用的工具集
1.2.2 JetBrains PyCharm 使用
JetBrains PyCharm 的介紹
PyCharm 是 JetBrains 公司針對Python推出的IDE(Integrated Development Environment,集成開發環境)。是目前最好的Python IDE之一。目前包含了兩個版本:
咱們推薦使用免費的社區版本,進行Python腳本的編寫和自動化測試執行。
PyCharm能夠在官網下載,http://www.jetbrains.com
PyCharm 安裝後,若是也安裝過 Python 環境,能夠直接進行操做。不然請在 1.2.3 中安裝好 Python,再使用 PyCharm。
安裝按照默認的步驟安裝
使用方式
Create New Project:
建立新的項目,選擇項目建立的位置,選擇Python的解釋器
C:\Pytho34
中,應該放到普通的目錄中新建Python文件
在建立的文件中編寫第一個Python語句
print("hello Python!")
右鍵該文件,選擇Run hello
,運行該語句,在下面的運行框中會顯示運行結果
C:\Python35\python.exe D:/Git/WeekendSelenium/untitled/hello.py hello python! Process finished with exit code 0
如圖
打開已經存在的項目,好比別人發給你的項目,或者已經建立過的項目
安裝後進行設置以下:
設置行號的顯示
在PyCharm 裏,顯示行號有兩種辦法:
臨時設置(不推薦)。右鍵單擊行號處,選擇 Show Line Numbers
。
可是這種方法,只對一個文件有效,而且,重啓PyCharm 後消失。
永久設置。File
--> Settings
-->Editor
-->Appearance
, 以後勾選Show Line Numbers
。
設置字體
選擇 Settings | Editor | Colors & Fonts | Fonts
Save AS 主題
選擇 Source Code Pro(建議選擇,等寬字體)
SVN / Git 在工具中的集成
源代碼管理工具(VCS, version control system)
若是TortoiseSVN版本低於
1.8
,須要先升級安裝1.8
以上的版本
選擇SVN(git)做爲代碼的源代碼管理工具。集成在PyCharm中的步驟以下
D:\SVN\XXProject\Trunck
2. 代碼沒有建立:在本地的SVN項目文件夾中新建項目,用PyCharm打開,提交。
3. 用PyCharm打開 剛剛部署的代碼 4. 選擇PyCharm的 `VCS`|`Enable VCS integration`,選擇 Subversion(svn) 或者 Git
5. 右鍵項目文件的根目錄,選擇 Subversion | add to VCS
6. 右鍵項目文件的根目錄,或者選 VCS | Commit Directory...
天天打開代碼後,右鍵項目文件的根目錄,首先 Subversion | update project
若是有衝突,先本地手工保存你作的修改(備份你的文件到其餘地方,SVN目錄以外的地方,而後Revert)
1.2.3 Selenium 的環境搭建
在 Windows 搭建和部署 Selenium 工具
主要包括兩個步驟:
安裝 Python 語言
Python的官方網站:http://www.python.org
Python 目前並行了兩套版本,2.x 和 3.x。若是你以前沒有 Python 的使用經驗,建議使用 Python 3.x 版本。兩套版本互相不兼容,而且 Python 從 3.5(含)開始,再也不支持 Windows XP 系統,請注意。
選擇安裝目錄
C:\python34
C:\python35
勾選添加環境變量
勾選Add Python.exe to PATH
安裝過程當中不要關閉彈出來的命令行窗口
關於 Python 的安裝,也能夠選擇一些第三方的Python 安裝包,典型的有 Anaconda3
,這樣的包有豐富的第三方庫,在使用 Python 的過程當中會更加方便。這裏向你們推薦一個測試交流圈q裙:1007119548。
Anaconda 的官網:https://www.continuum.io/anaconda-overview
安裝 Selenium 工具包
因爲 安裝好的 Python 默認有 pip
Python 包管理工具,能夠經過 pip
很是方便的安裝 Selenium。
啓動命令行工具:Win+R | 輸入 cmd | 回車
輸入命令:
pip install selenium
該命令的執行須要有互聯網聯網環境。此外該命令有如下幾種選項可使用
安裝指定的版本,例如安裝指定的 Selenium 3.4.3
pip install selenium==3.4.3
安裝最新版的 Selenium
pip install -U selenium # -U 也能夠用 --upgrade pip install --upgrade selenium
卸載安裝當前的 Selenium
pip uninstall selenium
固然,若是您的機器處於非接入互聯網的環境,您能夠事先下載 Selenium 的 Python 安裝包,再進行手動安裝。
上述地址會下載最新版的 Selenium,目前最早版的是 3.4.3,您也能夠根據如下路徑下載指定的 3.4.3
Selenium 3.4.3 下載地址:https://pypi.python.org/pypi/selenium/3.4.3#downloads
下載後,解壓該壓縮包
而後用命令行進入該壓縮包的根目錄,輸入命令進行安裝
python setup.py install
配置 瀏覽器 和 驅動
Selenium 2 能夠默認支持Firefox 46.0或者更低版本,對於其餘瀏覽器須要額外安裝驅動。
Selenium 3 對於全部的瀏覽器都須要安裝驅動,本文以 Chrome 和 Firefox、IE爲例設置瀏覽器和驅動。
ChromeDriver下載地址:http://chromedriver.storage.googleapis.com/index.html
ChromeDriver 與 Chrome 對應關係表:
ChromeDriver版本 | 支持的Chrome版本 |
---|---|
v2.31 | v58-60 |
v2.30 | v58-60 |
v2.29 | v56-58 |
v2.28 | v55-57 |
v2.27 | v54-56 |
v2.26 | v53-55 |
v2.25 | v53-55 |
v2.24 | v52-54 |
v2.23 | v51-53 |
v2.22 | v49-52 |
v2.21 | v46-50 |
v2.20 | v43-48 |
GeckoDriver下載地址:https://github.com/mozilla/geckodriver/releases
GeckoDriver 與 Firefox 的對應關係表:
GeckoDriver版本 | 支持的Firefox版本 |
---|---|
v0.18.0 | v56 |
v0.17.0 | v55 |
v0.16.0 | v54,須要Selenium 3.4或者以上 |
v0.15.0 | v53,須要Selenium 3.3或者以上 |
IEDriverServer下載地址:http://selenium-release.storage.googleapis.com/index.html
IEDriverServer 的版本須要與 Selenium 保持嚴格一致。
瀏覽器驅動的配置
經過上一節的環境安裝成功之後,咱們能夠進行第一個對Selenium 的使用,就是最簡腳本編寫。腳本以下:
# 聲明一個司機,司機是個Chrome類的對象 driver = webdriver.Chrome() # 讓司機加載一個網頁 driver.get("http://demo.ranzhi.org") # 給司機3秒鐘去打開 sleep(3) # 開始登陸 # 1\. 讓司機找用戶名的輸入框 we_account = driver.find_element_by_css_selector('#account') we_account.clear() we_account.send_keys("demo") # 2\. 讓司機找密碼的輸入框 we_password = driver.find_element_by_css_selector('#password') we_password.clear() we_password.send_keys("demo") # 3\. 讓司機找 登陸按鈕 並 單擊 driver.find_element_by_css_selector('#submit').click() sleep(3)
實際上一段20行的代碼,也不能算太少了。可是這段代碼的使用,確實體現了 Selenium 的最簡單的使用。咱們在下面內容進行闡述。
關於面向對象編程
經過前面的介紹,咱們知道 Selenium 支持多種語言,而且推薦使用面向對象的方式進行編程。接下來咱們將着重介紹如何使用面向對象的方式進行編程。
咱們利用 Python 進行面向對象編程,須要首先了解一個概念:類
類
類是任何面向對象編程的語言的基本組成,描述了使用的基本方法。咱們可能在目前,還不是特別明白類的含義,可是咱們能夠經過類的使用,來進一步瞭解。
類的使用
類,經過實例化進行使用。好比有一個類: Driver
,該類有一個方法: head(road)
那麼關於這個類的使用,只須要兩個步驟:
d = Driver()
d.head("中山路")
瞭解上述例子和使用之後,咱們來看具體的 Selenium 的使用。
這裏向你們推薦一個測試交流圈q裙:1007119548。
具體的對象的使用
在面向對象的理念看來,任何的編碼,都是由對象而來的,這裏也不例外。和以前介紹 WebDriver 時候的描述對應,咱們須要用到兩種主要的類,並將其實例化。
上述代碼中,使用了一個 WebDriver 類 的對象,即第2行,聲明瞭該類的對象,並賦值給變量 driver,接着變量 driver 做爲 WebDriver 類的對象,使用了多個 WebDriver 類的方法。
注意:Chrome 是 WebDriver 的子類,是 WebDriver 類的一種
we_account
,we_password
和最後一個匿名的對象,並經過產生的三個對象,調用 WebElement 類的方法
正是經過這樣的面向對象的方式,產生 Web司機(WebDriver類的對象),而且經過 Web司機不懈的努力,尋找到各類 Web元素(WebElement類的對象)進行操做,這樣便實現了 Selenium WebDriver 做爲一款出色的瀏覽器測試工具,進行瀏覽器UI界面的自動化測試的代碼編寫和用例執行。
經過上述最簡腳本的使用,咱們能夠來進一步瞭解 Selenium 的使用。事實上,上一節用的,即是 Selenium 的 WebDriver API。API(Application Programming Interface,應用程序編程接口,即經過編程語言,操做 WebDriver 的方法集合)
Selenium WebDriver API 官方參考:http://seleniumhq.github.io/selenium/docs/api/py/
具體API文檔地址:https://seleniumhq.github.io/selenium/docs/api/py/api.html
1.4.1 控制瀏覽器
瀏覽器的控制也是自動化測試的一個基本組成部分,咱們能夠將瀏覽器最大化,設置瀏覽器的高度和寬度以及對瀏覽器進行導航操做等。
# 瀏覽器打開網址 driver.get("https://www.baidu.com") # 瀏覽器最大化 driver.maximize_window() # 設置瀏覽器的高度爲800像素,寬度爲480像素 driver.set_window_size(480, 800) # 瀏覽器後退 driver.back() # 瀏覽器前進 driver.forward() # 瀏覽器關閉 driver.close() # 瀏覽器退出 driver.quit()
1.4.2 元素定位操做
WebDriver提供了一系列的定位符以便使用元素定位方法。常見的定位符有如下幾種:
那麼咱們如下的操做將會基於上述的定位符進行定位操做。
對於元素的定位,WebDriver API能夠經過定位簡單的元素和一組元素來操做。在這裏,咱們須要告訴Selenium如何去找元素,以致於他能夠充分的模擬用戶行爲,或者經過查看元素的屬性和狀態,以便咱們執行一系列的檢查。
在Selenium2中,WebDriver提供了多種多樣的find_element_by
方法在一個網頁裏面查找元素。這些方法經過提供過濾標準來定位元素。固然WebDriver也提供了一樣多種多樣的find_elements_by
的方式去定位多個元素。
儘管上述的方式,能夠進行元素定位,實際上咱們也是更多的用組合的方式進行元素定位。
方法Method | 描述Description | 參數Argument | 示例Example |
---|---|---|---|
id |
該方法經過ID的屬性值去定位查找單個元素 | id: 須要被查找的元素的ID | find_element_by_id('search') |
name |
該方法經過name的屬性值去定位查找單個元素 | name: 須要被查找的元素的名稱 | find_element_by_name('q') |
class name |
該方法經過class的名稱值去定位查找單個元素 | class_name: 須要被查找的元素的類名 | find_element_by_class_name('input-text') |
tag_name |
該方法經過tag的名稱值去定位查找單個元素 | tag: 須要被查找的元素的標籤名稱 | find_element_by_tag_name('input') |
link_text |
該方法經過連接文字去定位查找單個元素 | link_text: 須要被查找的元素的連接文字 | find_element_by_link_text('Log In') |
partial_link_text |
該方法經過部分連接文字去定位查找單個元素 | link_text: 須要被查找的元素的部分連接文字 | find_element_by_partial_link_text('Long') |
xpath |
該方法經過XPath的值去定位查找單個元素 | xpath: 須要被查找的元素的xpath | find_element_by_xpath('//*[@id="xx"]/a') |
css_selector |
該方法經過CSS選擇器去定位查找單個元素 | css_selector: 須要被查找的元素的ID | find_element_by_css_selector('#search') |
接下來的列表將會詳細展現find_elements_by
的方法集合。這些方法依據匹配的具體標準返回一系列的元素。
方法Method | 描述Description | 參數Argument | 示例Example |
---|---|---|---|
id |
該方法經過ID的屬性值去定位查找多個元素 | id: 須要被查找的元素的ID | find_elements_by_id('search') |
name |
該方法經過name的屬性值去定位查找多個元素 | name: 須要被查找的元素的名稱 | find_elements_by_name('q') |
class_name |
該方法經過class的名稱值去定位查找多個元素 | class_name: 須要被查找的元素的類名 | find_elements_by_class_name('input-text') |
tag_name |
該方法經過tag的名稱值去定位查找多個元素 | tag: 須要被查找的元素的標籤名稱 | find_elements_by_tag_name('input') |
link_text |
該方法經過連接文字去定位查找多個元素 | link_text: 須要被查找的元素的連接文字 | find_elements_by_link_text('Log In') |
partial_link_text |
該方法經過部分連接文字去定位查找多個元素 | link_text: 須要被查找的元素的部分連接文字 | find_elements_by_partial_link_text('Long') |
xpath |
該方法經過XPath的值去定位查找多個元素 | xpath: 須要被查找的元素的xpath | find_elements_by_xpath("//div[contains(@class,'list')]") |
css_selector |
該方法經過CSS選擇器去定位查找多個元素 | css_selector: 須要被查找的元素的ID | find_element_by_css_selector('.input_class') |
依據ID查找
請查看以下HTML的代碼,以便實現經過ID的屬性值去定義一個查找文本框的查找:
<input id="search" type="text" name="q" value="" class="input-text" maxlength="128" autocomplete="off"/>
根據上述代碼,這裏咱們使用find_element_by_id()
的方法去查找搜索框而且檢查它的最大長度maxlength
屬性。咱們經過傳遞ID的屬性值做爲參數去查找,參考以下的代碼示例:
def test_search_text_field_max_length(self): # get the search textbox search_field = self.driver.find_element_by_id("search") # check maxlength attribute is set to 128 self.assertEqual("128", search_field.get_attribute("maxlength"))
若是使用find_elements_by_id()
方法,將會返回全部的具備相同ID屬性值的一系列元素。
依據名稱name查找
這裏仍是根據上述ID查找的HTML代碼,使用find_element_by_name
的方法進行查找。參考以下的代碼示例:
# get the search textbox self.search_field = self.driver.find_element_by_name("q")
一樣,若是使用find_elements_by_name()
方法,將會返回全部的具備相同name屬性值的一系列元素。
依據class name查找
除了上述的ID和name的方式查找,咱們還可使用class name的方式進行查找和定位。
事實上,經過ID,name或者類名class name查找元素是最提倡推薦的和最快的方式。固然Selenium2 WebDriver也提供了一些其餘的方式,在上述三類方式條件不足,查找無效的時候,能夠經過這些其餘方式來查找。這些方式將會在後續的內容中講述。
請查看以下的HTML代碼,經過改代碼進行練習和理解.
<button type="submit" title="Search" class="button"> <span><span>Search</span></span> </button>
根據上述代碼,使用find_element_by_class_name()
方法去定位元素。
def test_search_button_enabled(self): # get Search button search_button = self.driver.find_element_by_class_name("button") # check Search button is enabled self.assertTrue(search_button.is_enabled())
一樣的若是使用find_elements_by_class_name()
方法去定位元素,將會返回全部的具備相同name屬性值的一系列元素。
依據標籤名tag name查找
利用標籤的方法相似於利用類名等方法進行查找。咱們能夠輕鬆的查找出一系列的具備相同標籤名的元素。例如咱們能夠經過查找表中的<tr>
來獲取行數。
下面有一個HTML的示例,這裏在無序列表中使用了<img>
標籤。
<ul class="promos"> <li> <a href="http://demo.magentocommerce.com/home-decor.html"> <img src="/media/wysiwyg/homepage-three-column-promo- 01B.png" alt="Physical & Virtual Gift Cards"> </a> </li> <li> <a href="http://demo.magentocommerce.com/vip.html"> <img src="/media/wysiwyg/homepage-three-column-promo- 02.png" alt="Shop Private Sales - Members Only"> </a> </li> <li> <a href="http://demo.magentocommerce.com/accessories/ bags-luggage.html"> <img src="/media/wysiwyg/homepage-three-columnpromo- 03.png" alt="Travel Gear for Every Occasion"> </a> </li> </ul>
這裏面咱們使用find_elements_by_tag_name()
的方式去獲取所有的圖片,在此以前,咱們將會使用find_element_by_class_name()
去獲取到指定的<ul>
。
具體代碼以下:
def test_count_of_promo_banners_images(self): # get promo banner list banner_list = self.driver.find_element_by_class_name("promos") # get images from the banner_list banners = banner_list.find_elements_by_tag_name("img") # check there are 20 tags displayed on the page self.assertEqual(20, len(banners))
依據連接文字link查找
連接文字查找一般比較簡單。使用find_element_by_link_text
請查看如下示例
<a href="#header-account" class="skip-link skip-account"> <span class="icon"></span> <span class="label">ACCOUNT Description</span> </a>
測試代碼以下:
def test_my_account_link_is_displayed(self): # get the Account link account_link = self.driver.find_element_by_link_text("ACCOUNT Description") # check My Account link is displayed/visible in # the Home page footer self.assertTrue(account_link.is_displayed())
依據部分連接文字partial text查找
這裏依舊使用上述的列子進行代碼編寫:
def test_account_links(self): # get the all the links with Account text in it account_links = self.driver.\ find_elements_by_partial_link_text("ACCOUNT") # check Account and My Account link is # displayed/visible in the Home page footer self.assertTrue(2, len(account_links))
依據XPath進行查找
XPath是一種在XML文檔中搜索和定位節點node的一種查詢語言。全部的主流Web瀏覽器都支持XPath。Selenium2能夠用強大的XPath在頁面中查找元素。
經常使用的XPath的方法有starts-with()
,contains()
和ends-with()
等
若想要了解更多關於XPath的內容,請查看http://www.w3schools.com/XPath/
以下有一段HTML代碼,其中裏面的<img>
沒有使用ID,name或者類屬性,因此咱們沒法使用以前的方法。亞這裏咱們能夠經過<img>
的alt
屬性,定位到指定的tag。
<ul class="promos"> <li> <a href="http://demo.magentocommerce.com/home-decor.html"> <img src="/media/wysiwyg/homepage-three-column-promo- 01B.png" alt="Physical & Virtual Gift Cards"> </a> </li> <li> <a href="http://demo.magentocommerce.com/vip.html"> <img src="/media/wysiwyg/homepage-three-column-promo- 02.png" alt="Shop Private Sales - Members Only"> </a> </li> <li> <a href="http://demo.magentocommerce.com/accessories/ bags-luggage.html"> <img src="/media/wysiwyg/homepage-three-columnpromo- 03.png" alt="Travel Gear for Every Occasion"> </a> </li> </ul>
具體代碼以下:
def test_vip_promo(self): # get vip promo image vip_promo = self.driver.\ find_element_by_xpath("//img[@alt='Shop Private Sales - Members Only']") # check vip promo logo is displayed on home page self.assertTrue(vip_promo.is_displayed()) # click on vip promo images to open the page vip_promo.click() # check page title self.assertEqual("VIP", self.driver.title)
固然,若是使用find_elements_by_xpath()
的方法,將會返回全部匹配了XPath查詢的元素。
依據CSS選擇器進行查找
CSS是一種設計師用來描繪HTML文檔的視覺的層疊樣式表。通常來講CSS用來定位多種多樣的風格,同時能夠用來是一樣的標籤使用一樣的風格等。相似於XPath,Selenium2也可使用CSS選擇器來定位元素。
請查看以下的HTML文檔。
<div class="minicart-wrapper"> <p class="block-subtitle">Recently added item(s) <a class="close skip-link-close" href="#" title="Close">×</a> </p> <p class="empty">You have no items in your shopping cart. </p> </div>
咱們來建立一個測試,驗證這些消息是否正確。
def test_shopping_cart_status(self): # check content of My Shopping Cart block on Home page # get the Shopping cart icon and click to open the # Shopping Cart section shopping_cart_icon = self.driver.\ find_element_by_css_selector("div.header-minicart span.icon") shopping_cart_icon.click() # get the shopping cart status shopping_cart_status = self.driver.\ find_element_by_css_selector("p.empty").text self.assertEqual("You have no items in your shopping cart.", shopping_cart_status) # close the shopping cart section close_button = self.driver.\ find_element_by_css_selector("div.minicart-wrapper a.close") close_button.click()
特殊 iframe 操做
iframe 元素會建立包含另一個文檔的內聯框架(即行內框架)。
iframe: 紫禁城
在一個<html>中,包含了另外一個<html>
示例
<html> <head> <title>iframe示例</title> </head> <body> <h1> 這裏是H1,標記了標題 </h1> <p> 這裏是段落,標記一個段落,屬於外層 </p> <div> <iframe id="iframe-1"> <html> <body> <p> 這裏是個段落,屬於內層,內聯框架中的 </p> <div id="div-1"> <p class="hahahp"> 這裏是div中的段落,須要被定位 </p> </div> </body> </html> </iframe> </div> </body> </html>
須要定位上面示例中的<p>
:這裏是div中的段落,須要被定位
以下是selenium WebDiriver的代碼
## 查找並定位 iframe element_frame = driver.find_element_by_css_selector('#iframe-1') ## 切換到剛剛查找到的 iframe driver.switch_to.frame(element_frame) ## 定位 <p> driver.find_element_by_css_selector('#div-1 > p') ## TODO.... ## 退出剛剛切換進去的 iframe driver.switch_to.default_content()
特殊 Select 操做
<select>
是選擇列表Select 是個selenium的類
selenium.webdriver.support.select.Select
Select 類的路徑:
C:\Python35\Lib\site-packages\selenium\webdriver\support\select.py
<select id="brand"> <option value ="volvo">Volvo</option> <option value ="saab">Saab</option> <option value="opel">Opel</option> <option value="audi">Audi</option> </select>
示例,選擇 Audi
## 查找並定位到 select element_select = driver.find_element_by_css_selector('#brand') ## 用Select類的構造方法,實例化一個對象 object_select object_select = Select(element_select) ## 操做 object_select object_select.select_by_index(3) ## 也能夠這樣 object_select.select_by_value('audi') ## 還能夠這樣 object_select.select_by_visible_text('Audi')
組合操做
自動化經驗的積累,須要100%按照手工的步驟進行操做。
好比步驟以下:
<a id="customer_chosen">
<ul id="customer_list">
<ul>
的第五個<li>
代碼示例
driver.find_element_by_css_selector('#customer_chosen').click() sleep(1) driver.find_element_by_css_selector('#customer_list > li:nth-child(5)')
1.4.3 鼠標事件操做
Web測試中,有關鼠標的操做,不僅是單擊,有時候還要作右擊、雙擊、拖動等操做。這些操做包含在ActionChains類中。
經常使用的鼠標方法:
例子:
# 方法模擬鼠標右鍵,參考代碼以下: # 引入ActionChains 類 from selenium.webdriver.common.action_chains import ActionChains ... # 定位到要右擊的元素 right =driver.find_element_by_xpath("xx") # 對定位到的元素執行鼠標右鍵操做 ActionChains(driver).context_click(right).perform() ... # 定位到要雙擊的元素 double = driver.find_element_by_xpath("xxx") # 對定位到的元素執行鼠標雙擊操做 ActionChains(driver).double_click(double).perform()
1.4.4 鍵盤事件操做
鍵盤操做常常處理的以下:
代碼 | 描述 |
---|---|
send_keys(Keys.BACKSPACE) |
刪除鍵(BackSpace) |
send_keys(Keys.SPACE) |
空格鍵(Space) |
send_keys(Keys.TAB) |
製表鍵(Tab) |
send_keys(Keys.ESCAPE) |
回退鍵(Esc) |
send_keys(Keys.ENTER) |
回車鍵(Enter) |
send_keys(Keys.CONTROL,'a') |
全選(Ctrl+A) |
send_keys(Keys.CONTROL,'c') |
複製(Ctrl+C) |
代碼以下
from selenium import webdriver # 引入Keys 類包 from selenium.webdriver.common.keys import Keys import time driver = webdriver.Chrome() driver.get("http://www.baidu.com") # 輸入框輸入內容 driver.find_element_by_id("kw").send_keys("selenium") time.sleep(3) # 刪除多輸入的一個m driver.find_element_by_id("kw").send_keys(Keys.BACK_SPACE) time.sleep(3) # 輸入空格鍵+「教程」 driver.find_element_by_id("kw").send_keys(Keys.SPACE) driver.find_element_by_id("kw").send_keys("教程") time.sleep(3) # ctrl+a 全選輸入框內容 driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
1.4.5 截圖操做
截圖的方法:save_screenshot(file)
在上一節,咱們對 Selenium WebDriver 的使用,僅僅停留在讓網頁自動的進行操做的階段,並無對任何一個步驟進行「檢查」。固然,這樣沒有「檢查」的操做,其實是沒有測試意義的。那麼第一項,咱們須要解決的即是「檢查」的問題。
所謂「檢查」,實際上就是斷言。對須要檢查的步驟操做,經過對預先設置的指望值,和執行結果的實際值之間的對比,獲得測試的結果。在這裏,咱們並不須要單獨的寫 if
語句進行各類斷定,而是可使用編程語言中對應的單元測試框架,便可解決好此類問題。
目前 Java 語言主流的單元測試框架有 JUnit 和 TestNG。Python 語言主流的單元測試框架有 unittest 。本小節的內容,主要介紹 unittest 的使用,探討單元測試框架如何幫助自動化測試。
接下來咱們將會使用 Python 語言的unittest
框架展開「檢查」。unittest
框架的本來的名字是PyUnit。是從JUnit 這樣一個被普遍使用的 經典的Java應用開發的單元測試框架創造而來。相似的框架還有NUnit(.Net開發的單元測試框架)等。咱們可使用unittest框架爲任意Python項目編寫可理解的單元測試集合。如今這個unittest已經做爲Python的標準庫模塊發佈。咱們安裝完Python之後,即可以直接使用unittest。
使用unittest須要如下簡單的三步:
test
開頭unittest 並未使用 Java 語言常見的註解方式,依舊停留在 比較早期的 Java 版本中依靠方法名稱進行識別的方式。主要有如下兩個固定名字的方法:
具體的代碼以下:
## 引入unittest模組 import unittest ## 定義測試類,名字爲DemoTests ## 該類必須繼承unittest.TestCase基類 class DemoTests(unittest.TestCase): ## 使用'@'修飾符,註明該方法是類的方法 ## setUpClass方法是在執行測試以前須要先調用的方法 ## 是開始測試前的初始化工做 @classmethod def setUpClass(cls): print("call setUpClass()") ## 每個測試開始前的預置條件 def setUp(self): print("call setUp()") ## 每個測試結束之後的清理工做 def tearDown(self): print("call tearDown()") ## 測試一(務必以test開頭) def test_01(self): print("call test_01()") pass ## 測試三(務必以test開頭) def test_02(self): print("call test_02()") pass ## 測試三(務必以test開頭) def test_03(self): print("call test_03()") pass ## tearDownClass方法是執行完全部測試後調用的方法 ## 是測試結束後的清除工做 @classmethod def tearDownClass(cls): print("call tearDownClass()") # 執行測試主函數 if __name__ == '__main__': ## 執行main全局方法,將會執行上述全部以test開頭的測試方法 unittest.main(verbosity=2)
須要注意步驟:
上述代碼運行結果以下:
call setUpClass() call setUp() call test_01() call tearDown() call setUp() call test_02() call tearDown() call setUp() call test_06() call tearDown() call tearDownClass()
爲何選擇 unittest
unittest 的斷言配置使用
unittest 的斷言,屬於 TestCase
類,只要繼承了該類,都可以經過 self調用斷言
方法 Method | 檢查條件 |
---|---|
assertEqual(a, b [, msg]) |
a == b,msg可選,用來解釋失敗的緣由 |
assertNotEqual(a, b [, msg] |
a != b,msg可選,用來解釋失敗的緣由 |
assertTrue(x [, msg]) |
x 是真,msg可選,用來解釋失敗的緣由 |
assertFalse(x [, msg]) |
x 是假,msg可選,用來解釋失敗的緣由 |
assertIsNot(a, b [, msg]) |
a 不是 b,msg可選,用來解釋失敗的緣由 |
什麼是封裝
封裝是一個面向對象編程的概念,是面向對象編程的核心屬性,經過將代碼內部實現進行密封和包裝,從而簡化編程。對Selenium進行封裝的好處主要有以下三個方面:
關鍵方法的封裝思路
封裝的具體示例:
找到一個指定輸入框(selector),而且輸入指定的字符(text)
type(selector, text)
不用在業務邏輯中,使用屢次的 find_element_by_id(...))
def type(self, selector, text): """ Operation input box. Usage: driver.type("i,el","selenium") """ el = self._locate_element(selector) el.clear() el.send_keys(text)
找到一個能夠點擊的元素(selector),而且點擊(click)
click(selector)
def click(self, selector): """ It can click any text / image can be clicked Connection, check box, radio buttons, and even drop-down box etc.. Usage: driver.click("i,el") """ el = self._locate_element(selector) el.click()
找到一個指定的frame,而且切換進去
switch_to_frame(selector)
def switch_to_frame(self, selector): """ Switch to the specified frame. Usage: driver.switch_to_frame("i,el") """ el = self._locate_element(selector) self.base_driver.switch_to.frame(el)
select_by_index(selector, index)
def select_by_index(self, selector, index): """ It can click any text / image can be clicked Connection, check box, radio buttons, and even drop-down box etc.. Usage: driver.select_by_index("i,el") """ el = self._locate_element(selector) Select(el).select_by_index(index)
以上的代碼是封裝了_locate_element()
的幾種方法,在具體使用封裝過的代碼的時候,只須要簡單的調用便可。接下來的重點,是介紹 _locate_element(selector)
的封裝方式。
find_element_by_...)
selector
顯示出分類selector
中須要包含一個特殊符號硬編碼 hard code
來實例化,例如 ,
或者 ?
或者 其餘很是用字符 =>
this.byChar
要把查找到元素的返回給調用的地方:必需要有返回值,類型是 WebElement
def _locate_element(self, selector): """ to locate element by selector :arg selector should be passed by an example with "i,xxx" "x,//*[@id='langs']/button" :returns DOM element """ if self.by_char not in selector: return self.base_driver.find_element_by_id(selector) selector_by = selector.split(self.by_char)[0].strip() selector_value = selector.split(self.by_char)[1].strip() if selector_by == "i" or selector_by == 'id': element = self.base_driver.find_element_by_id(selector_value) elif selector_by == "n" or selector_by == 'name': element = self.base_driver.find_element_by_name(selector_value) elif selector_by == "c" or selector_by == 'class_name': element = self.base_driver.find_element_by_class_name(selector_value) elif selector_by == "l" or selector_by == 'link_text': element = self.base_driver.find_element_by_link_text(selector_value) elif selector_by == "p" or selector_by == 'partial_link_text': element = self.base_driver.find_element_by_partial_link_text(selector_value) elif selector_by == "t" or selector_by == 'tag_name': element = self.base_driver.find_element_by_tag_name(selector_value) elif selector_by == "x" or selector_by == 'xpath': element = self.base_driver.find_element_by_xpath(selector_value) elif selector_by == "s" or selector_by == 'css_selector': element = self.base_driver.find_element_by_css_selector(selector_value) else: raise NameError("Please enter a valid type of targeting elements.") return element
面向對象編程思想的運用
封裝後的方法如何被調用
使用上面的封裝類,就須要指定特定的 selector
類型 | 示例(分隔符以逗號, 爲例) |
描述 |
---|---|---|
id | "account" 或者 "i,account" 或者 "id,account" | 分隔符左右兩側不能夠空格 |
xpath | "x,//*[@id="s-menu-dashboard"]/button/i" | |
css selector | "s,#s-menu-dashboard > button > i" | |
link text | "l,退出" | |
partial link text | "p,退" | |
name | "n,name1" | |
tag name | "t,input" | |
class name | "c,dock-bottom | |
具體調用示例
def login(self, account, password, keep): """ 登陸系統 :param account: :param password: :param keep: :return: 返回保持登陸複選框的 checked 值 """ self.base_driver.type(self.LOGIN_ACCOUNT_SELECTOR, account) self.base_driver.type(self.LOGIN_PASSWORD_SELECTOR, password) current_checked = self.get_current_keep_value() if keep: if current_checked is None: self.base_driver.click(self.LOGIN_KEEP_SELECTOR) else: if current_checked == "true": self.base_driver.click(self.LOGIN_KEEP_SELECTOR) actual_checked = self.get_current_keep_value() self.base_driver.click(self.LOGIN_SUBMIT_SELECTOR) sleep(2) return actual_checked
Page-Object設計模式的本質
Page Object設計模式是Selenium自動化測試項目的最佳設計模式之一,強調測試、邏輯、數據和驅動相互分離。
Page Object模式是Selenium中的一種測試設計模式,主要是將每個頁面設計爲一個Class,其中包含頁面中須要測試的元素(按鈕,輸入框,標題等),這樣在Selenium測試頁面中能夠經過調用頁面類來獲取頁面元素,這樣巧妙的避免了當頁面元素id或者位置變化時,須要改測試頁面代碼的狀況。當頁面元素id變化時,只須要更改測試頁Class中頁面的屬性便可。
它的好處以下:
具體的作法以下:
Page 如何劃分
通常經過繼承的方式,進行按照實際Web頁面進行劃分
Page-Object 類如何實現
實現的示例
Page 基類
設計了一個基本的 Page類,以便全部的頁面進行繼承,該類標明瞭一個sub page類的基本功能和公共的功能。
全局變量: self.base_driver,讓全部的子類都使用的。
# 基類的變量,全部繼承的類,均可以使用 base_driver = None
構造方法:
傳遞 driver的構造方法
# 方法 def __init__(self, driver: BoxDriver): """ 構造方法 :param driver: ":BoxDriver" 規定了 driver 參數類型 """ self.base_driver = driver
LOGIN_ACCOUNT_SELECTOR = "s, #account" LOGIN_PASSWORD_SELECTOR = "s, #password" LOGIN_KEEP_SELECTOR = "s, #keepLoginon" LOGIN_SUBMIT_SELECTOR = "s, #submit" LOGIN_LANGUAGE_BUTTON_SELECTOR = "s, #langs > button" LOGIN_LANGUAGE_MENU_SELECTOR = "s, #langs > ul > li:nth-child(%d) > a" LOGIN_FAIL_MESSAGE_SELECTOR = "s, body > div.bootbox.modal.fade.bootbox-alert.in > div > div > div.modal-body"
成員方法:
每一個子類都須要的系統功能:
open
def open(self, url): """ 打開頁面 :param url: :return: """ self.base_driver.navigate(url) self.base_driver.maximize_window() sleep(2)
全部子類(頁面)都具備的業務功能
* select_app * logout
Sub Pages(s)子類
具體的頁面的類,定義了某個具體的頁面的功能
必須繼承基類
class MainPage(BasePage):
特定頁面的業務
使用基類的 self.base_driver
成員變量
Tests 類
這部分描述的是具體的測試用例。
聲明全局變量
base_driver = None base_url = None main_page = None
調用各類頁面(pages)
實例化Page
self.main_page = MainPage(self.base_driver)
使用page的對象,調用成員方法
self.main_page.open(self.base_url) self.main_page.change_language(lang)
什麼是數據驅動
主要的數據驅動方式有兩種:
經過 CSV 文件 或者 MySQL 數據庫,是主流的數據驅動方式。固然數據驅動也能夠結合單元測試框架的參數化測試進行編寫(此部分本文不作具體描述)。
不管使用了 哪種(CSV 或者 MySQL),讀取數據後都要進行遍歷操做。
使用 csv
import csv csv_file = open("xxx.csv", "r", encoding="utf8") csv_data = csv.reader(csv_file) for row in csv_data: # 進行測試 # 使用字典類型 data_to_test = { "key1": row[0], "key2": row[1] } csv_file.close()
使用 MySQL
import pymysql connect = pymysql.connect(host="xx", port=3306, user="root", passwd="xxx", db="xx") cur = connect.cursor() cur.execute("SELECT...") mysql_data = cur.fetchall() for row in mysql_data: # 進行測試 # 使用字典類型 data_to_test = { "key1": row[0], "key2": row[1] } cur.close() connect.close()
須要掌握的知識點:
dict
類型如何生成測試報告
測試報告的種類
HTML 測試報告的生成
HTML測試報告須要引入HTMLTestRunner
HTMLTestRunner是基於Python2.7的,咱們的課程講義基於Python3.x,那麼須要對這個文件作必定的修改。
測試的示例代碼以下
# 聲明一個測試套件 suite = unittest.TestSuite() # 添加測試用例到測試套件 suite.addTest(RanzhiTests("test_ranzhi_login")) # 建立一個新的測試結果文件 buf = open("./result.html", "wb") # 聲明測試運行的對象 runner = HTMLTestRunner.HTMLTestRunner(stream=buf, title="Ranzhi Test Result", description="Test Case Run Result") # 運行測試,而且將結果生成爲HTML runner.run(suite) # 關閉文件輸出 buf.close()
在這裏向你們推薦一個學習交流羣,裏面有小夥伴整理好的自動化資料上傳在羣文件,歡迎你們加羣下載:1007119548,
若是以爲這篇文章不錯也能夠轉發給須要的朋友的哦!