洞見 SELENIUM 自動化測試

洞見SELENIUM自動化測試

  • 第一部分:使用測試工具從自動化測試的定義開始,主要討論自動化測試如何開展的話題。
  • 第一部分:構建測試方案在熟悉自動化測試的基礎上,主要討論如何構建優秀的自動化測試。

寫在最前面:目前自動化測試並不屬於新鮮的事物,或者說自動化測試的各種方法論已經層出不窮,但是,能夠在項目中持之以恆的實踐自動化測試的團隊,卻依舊不是非常多。有的團隊知道怎麼做,做的還不夠好;有的團隊還正在探索和摸索怎麼做,甚至還有一些多方面的技術上和非技術上的舊系統需要重構……

本文將會從使用實踐兩個視角,嘗試對基於Web UI自動化測試做細緻的分析和解讀,給各位去思考和實踐做一點引路,以便各團隊能找到更好的方式。

1. 使用測試工具

《論語》有云:工欲善其事,必先利其器。在開始具體的自動化測試之前,我們需要做好更多的準備,包括以下幾個方面:

  • 認識自動化測試
  • 準備自動化測試工具
  • 使用有效的方式
  • 針對具體的測試對象

接下來的第一部分內容,我們將會從上述的幾個方面進行探討。

1.1 自動化測試理論介紹

  • 自動化測試的5W

正如開篇所提到的,自動化測試不再是一個陌生的話題,而是一個具體的存在。作為測試實踐活動的一部分,我們首先分析一下自動化測試的方方面面。

    • WHAT, 什麼是自動化測試

G.J.Myers在其經典的著作《軟體測試藝術》(The Art of Software Testing)一書中,給出了測試的定義:

「程序測試是為了發現錯誤而執行的過程。」

這個概念產生於30年前,對軟體測試的認識還非常有局限性,當然也是因為受瀑布開發模型的影響,認為軟體測試是編程之後的一個階段。只有等待代碼開發出來以後,通過執行程序,像用戶那樣操作軟體去發現問題。

自動化測試:以人為驅動的測試行為轉化為機器執行的一種過程

自動化測試,就是把手工進行的測試過程,轉變成機器自動執行的測試過程。該過程,依舊是為了發現錯誤而執行。因此自動化測試的關鍵在於「自動化」三個字。自動化測試的內容,也就相應的轉變成如何「自動化」去實現原本手工進行的測試的過程。

所有的「自動化」,依靠的無疑都是程序。

通過程序,可以把手工測試,轉變成自動化測試。

    • WHEN, 在什麼時候開展自動化測試

自動化測試的開展,依賴於「程序」。那麼程序,其實就是由「源代碼」構建而來的。那麼原則上,只要能做出自動化測試所需要的「程序」的時候,變可以進行自動化測試。但往往,並不是所有的「時候」都是好的「時機」。從這個W開始,我們將會加入對於成本的顧慮,也正是因為「成本」的存在,才使得下面的討論,變得有意義。

所有的開銷,都是有成本的。構建成「程序」的源代碼,也是由工程師寫出來的。那麼需要考慮這個過程中的成本。基於這個考慮,在能夠比較穩定的構建「程序」的時候,不需要花費太多開銷在「源代碼」的時候,就是開展自動化測試的好時機。這個開銷包括編寫修改源代碼,而源代碼指的是構建出用來做自動化測試的程序的源代碼。

    • WHERE, 在什麼地方進行自動化測試

自動化測試的執行,依靠的是機器。那麼自動化測試必將在「機器」上進行。一般來說,這個機器包括桌面電腦和伺服器。通過將寫好的源代碼部署在機器上,構建出用來做自動化測試的"程序",並且運行該程序,實現自動化測試。

    • WHICH, 對什麼目標進行自動化測試

自動化測試的目標,是被測試的軟體。拋開人工智慧的成分,手工測試必將在「人工智慧」足夠普及和足夠「智能」之前,替代一大部分不需要「人類智能」的手工測試;以及自動化測試會做一些手工測試無法實施的,或者手工測試無法覆蓋的測試。

      • 不需要「人類智能」的普通手工測試
        • 界面的普通操作
        • 通過固定輸入和固定操作而進行的流程化測試
        • 重複的普通測試
      • 手工測試無法實施或者覆蓋的
        • 大量的數據的輸入
        • 大量的步驟的操作
        • 源代碼基本的測試
        • 系統模塊間介面的調用測試
        • ……
    • HOW, 如何開展自動化測試

和所有的其他測試一樣,自動化測試的流程也是由「用例」執行和「缺陷」驗證組成。差別是需要找到合適的「工具」來替代「人手」。不同目標的自動化測試有不同的測試工具,但是任何工具都無不例外的需要「編程」的過程,實現「源代碼」,也可以稱之為測試腳本。於是開展自動化測試的方式基本上如下:

      • 準備測試用例
      • 找到合適的自動化測試工具
      • 用準確的編程形成測試腳本
      • 在測試腳本中對目標進行「檢查」,即做斷言
      • 記錄測試日誌,生成測試結果
  • 自動化測試的典型金字塔原理

談到自動化測試,就不得不提的一個人和概念就是:Martin Fowler和他的金字塔原理。首先請看金字塔原理的圖示如下:

該圖說明了三個問題:

    • 自動化測試包括三個方面:UI前端界面,Service服務契約和Unit底層單元
    • 越是底層的測試,運行速度越快,時間開銷越少,金錢開銷越少
    • 越是頂層的測試,運行速度越慢,時間開銷越多,金錢開銷越多

這是理想中的金字塔原理。

在實際的項目中,尤其是結合國內的項目實踐,其實還隱藏了另一個問題:越是頂層的測試,效果越明顯。有句話說「貴的東西,除了貴,其他都是好的!」能夠很清晰的闡述這個觀點。

金字塔原理在國內的適應性也有一定的問題

    • 自動化測試的起步不是特別早
    • 甚至軟體測試很長一段時間都在進行基於業務的手工測試,測試人員的代碼能力相對較弱
    • 開發人員在代碼中不太習慣寫單元測試
    • 近些年基於服務契約的API測試也在興起

相對來說,在基於UI前端界面的自動化測試反倒是開展和實施的不是特別多。儘管基於界面的測試帶來的效果還是能夠立竿見影的。對於產品的質量提升,還是比較容易有保證。

  • 自動化測試的適用範圍

自動化測試可以涉及和試用的範圍主要在以下方面:

    • 基於Web UI的瀏覽器應用的界面測試
    • 基於WebService或者WebAPI的服務契約測試
    • 基於WCF.net remotingSpring等框架的服務的集成測試
    • 基於APP UI的移動應用界面測試
    • 基於JavaC#等編程文件進行的單元測試

本文集中討論第一條:基於Web UI的瀏覽器應用的界面測試。界面的改動對於測試來說,具有較大的成本風險。主要考慮以下方面:

    • 任務測試明確,不會頻繁變動
    • 每日構建後的測試驗證
    • 比較頻繁的回歸測試
    • 軟體系統界面穩定,變動少
    • 需要在多平台上運行的相同測試案例、組合遍歷型的測試、大量的重複任務
    • 軟體維護周期長
    • 項目進度壓力不太大
    • 被測軟體系統開發比較規範,能夠保證系統的可測試性
    • 具備大量的自動化測試平台
    • 測試人員具備較強的編程能力
  • 自動化測試的流程

自動化測試和普通的手工測試遵循的測試流程,與項目的具體實踐相關。一般來說,也是需要從測試計劃開始涉及自動化測試的。

    • 測試計劃:劃定自動化測試的範圍包含哪些需求,涉及到哪些測試過程
    • 測試策略:確定自動化測試的工具、編程方案、代碼管理、測試重點
    • 測試設計:使用測試設計方法對被測試的需求進行設計,得出測試的測試點、用例思維導圖等
    • 測試實施:根據測試設計進行用例編寫,並且將測試用例用編程的方式實現測試腳本
    • 測試執行:執行測試用例,運行測試腳本,生成測試結果

1.2 自動化測試工具

基於Web UI的自動化測試工具主要有兩大類:付費的商業版工具和免費使用的開源版工具。典型的有兩種:

  • UFT,QTP被惠普收購以後的新名稱。
    • 通過程序的錄製,可以實現測試的編輯
    • 錄製的測試腳本是 VBScript 語法
    • 成熟版的商業付費工具
    • 工具比較龐大,對具體的項目定製測試有難度
  • SELENIUM,本次選擇的開源工具
    • 本身不是測試工具,只是模擬瀏覽器操作的工具
    • 背後有 Google 維護源代碼
    • 支持全部主流的瀏覽器
    • 支持主流的編程語言,包括:Java、Python、C#、PHP、Ruby、JavaScript等
    • 工具很小,可以實現對測試項目的定製測試方案
    • 基於標準的 WebDriver 語法規範

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 官網:seleniumhq.org/
    • Selenium Github 主頁:github.com/SeleniumHQ/s

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 有一個記錄功能,能記錄用戶的操作,並且能選擇多種語言把它們導出到一個可重用的腳本中用於後續執行。

    • Selenium RC

Selenium RC 是selenium 家族的核心工具,Selenium RC 支持多種不同的語言編寫自動化測試腳本,通過selenium RC 的伺服器作為代理伺服器去訪問應用從而達到測試的目的。

selenium RC 使用分Client Libraries 和Selenium Server。

      • Client Libraries 庫主要主要用於編寫測試腳本,用來控制selenium Server 的庫。
      • Selenium Server 負責控制瀏覽器行為,總的來說,Selenium Server 主要包括3 個部分:Launcher、Http Proxy、Core。
    • 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 語言的選擇,便捷
    • 測試人員的編程能力普遍不是很強,而Python作為一種腳本語言,不僅功能強大,而且語法優美,支持多種自動化測試工具,而且學習上手比較容易。
    • Python的社區發展比較好,有著非常多的文檔和支持庫,另外Python也可以在Web開發、數據處理、科學計算等縱多領域有著非常好的應用前景。
    • 對於有一定編程基礎的人員,使用Python作為自動化測試的語言可以非常順暢的轉換,幾乎沒有學習成本。同時Python是標準的面向對象的編程語言,對於C#、Java等面向對象的語言有著非常好的示例作用,通過Python的示例可以非常輕鬆的觸類旁通,使用其他語言進行Selenium2.0的WebDriver的使用。
    • 讀音:/pa?θ?n/
    • Python的創始人為Guido Van Rossum。1989年聖誕節期間,在阿姆斯特丹,Guido為了打發聖誕節的無趣,決心開發一個新的腳本解釋程序,做為ABC 語言的一種繼承。之所以選中Python(大蟒蛇的意思)作為程序的名字,是因為他是一個叫Monty Python的喜劇團體的愛好者。
    • Python 語言除了在自動化測試領域有出色的表現外,在系統編程,網路編程,web 開發,GUI開發,科學計算,遊戲開發等多個領域應用非常廣泛,而且具有非常良好的社區支持。也就是說學習和掌握python 編程,其實是為你打開了一道更廣闊的大門。
  • 使用的工具集
    • IDE: Jetbrains PyCharm
    • 語言: Python
    • 工具: Selenium WebDriver
    • 源代碼管理: SVN/Git

1.2.2 JetBrains PyCharm 使用

  • JetBrains PyCharm 的介紹

PyCharm 是 JetBrains 公司針對Python推出的IDE(Integrated Development Environment,集成開發環境)。是目前最好的Python IDE之一。目前包含了兩個版本:

    • 社區版,Community Edition
    • 專業版,Professional Edition
      • 付費
      • 比社區版主要多了Web開發框架

我們推薦使用免費的社區版本,進行Python腳本的編寫和自動化測試執行。

PyCharm可以在官網下載,http://www.jetbrains.com

PyCharm 安裝後,如果也安裝過 Python 環境,可以直接進行操作。否則請在 1.2.3 中安裝好 Python,再使用 PyCharm。

    • 安裝按照默認的步驟安裝
      • 使用方式
        • Create New Project:

創建新的項目,選擇項目創建的位置,選擇Python的解釋器

          • 設置location,項目的路徑和名稱
          • 名稱必須以英文字母開頭
          • 名稱不可以有空格
          • 位置不可以在 C:Pytho34中,應該放到普通的目錄中
          • 設置interpreter
          • 一個電腦可以裝多個 Python
          • 這裡選擇一個你需要的 Pythpn

新建Python文件

在創建的文件中編寫第一個Python語句

print("hello Python!")

右鍵該文件,選擇Run hello,運行該語句,在下面的運行框中會顯示運行結果

C:Python35python.exe D:/Git/WeekendSelenium/untitled/hello.py hello python! Process finished with exit code 0

如圖

        • Open

打開已經存在的項目,比如別人發給你的項目,或者已經創建過的項目

    • 安裝後進行設置如下:
      • 設置行號的顯示

在PyCharm 里,顯示行號有兩種辦法:

        1. 臨時設置(不推薦)。右鍵單擊行號處,選擇 Show Line Numbers

但是這種方法,只對一個文件有效,並且,重啟PyCharm 後消失。

        1. 永久設置。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中的步驟如下

    1. 代碼已經存在在SVN repo中:把代碼放到SVN在本地簽出(check out)的文件夾目錄中,例如 D:SVNXXProjectTrunck

    1. 代碼沒有創建:在本地的SVN項目文件夾中新建項目,用PyCharm打開,提交。

    1. 用PyCharm打開 剛剛部署的代碼
    2. 選擇PyCharm的 VCS|Enable VCS integration,選擇 Subversion(svn) 或者 Git

    1. 右鍵項目文件的根目錄,選擇 Subversion | add to VCS

    1. 右鍵項目文件的根目錄,或者選 VCS | Commit Directory...

    1. 每天打開代碼後,右鍵項目文件的根目錄,首先 Subversion | update project
    2. 如果有衝突,先本地手工保存你做的修改(備份你的文件到其他地方,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 系統,請注意。

      • 選擇安裝目錄
        • 3.4或者3.4以下的版本,都是 C:python34
        • 3.5以上的目錄,默認裝個人文件夾,建議用類似上面的目錄,比如C:python35

      • 勾選添加環境變數

勾選Add Python.exe to PATH

      • 安裝過程中不要關閉彈出來的命令行窗口
      • 關於 Python 的安裝,也可以選擇一些第三方的Python 安裝包,典型的有 Anaconda3,這樣的包有豐富的第三方庫,在使用 Python 的過程中會更加方便。

Anaconda 的官網:continuum.io/anaconda-o

    • 安裝 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 安裝包,再進行手動安裝。
        • 官方下載地址:pypi.python.org/pypi/se
        • 上述地址會下載最新版的 Selenium,目前最先版的是 3.4.3,您也可以根據以下路徑下載指定的 3.4.3
        • Selenium 3.4.3 下載地址:pypi.python.org/pypi/se
        • 下載後,解壓該壓縮包
        • 然後用命令行進入該壓縮包的根目錄,輸入命令進行安裝

python setup.py install

  • 配置 瀏覽器 和 驅動
    • Selenium 2 可以默認支持Firefox 46.0或者更低版本,對於其他瀏覽器需要額外安裝驅動。
    • Selenium 3 對於所有的瀏覽器都需要安裝驅動,本文以 Chrome 和 Firefox、IE為例設置瀏覽器和驅動。
      • ChromeDriver下載地址:chromedriver.storage.googleapis.com
      • ChromeDriver 與 Chrome 對應關係表:

ChromeDriver版本支持的Chrome版本v2.31v58-60v2.30v58-60v2.29v56-58v2.28v55-57v2.27v54-56v2.26v53-55v2.25v53-55v2.24v52-54v2.23v51-53v2.22v49-52v2.21v46-50v2.20v43-48

      • GeckoDriver下載地址:github.com/mozilla/geck
      • GeckoDriver 與 Firefox 的對應關係表:

GeckoDriver版本支持的Firefox版本v0.18.0v56v0.17.0v55v0.16.0v54,需要Selenium 3.4或者以上v0.15.0v53,需要Selenium 3.3或者以上

      • IEDriverServer下載地址:selenium-release.storage.googleapis.com
      • IEDriverServer 的版本需要與 Selenium 保持嚴格一致。
    • 瀏覽器驅動的配置
      • 首先,將下載好的對應版本的瀏覽器安裝。
      • 其次,在 Python 的根目錄中,放入瀏覽器驅動。
      • 最好再重啟電腦,一般情況下不重啟也可以的。

1.3 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)

那麼關於這個類的使用,只需要兩個步驟:

      1. 實例化該類:d = Driver()
      2. 調用類的方法:d.head("中山路")
    • 了解上述例子和使用以後,我們來看具體的 Selenium 的使用。

  • 具體的對象的使用

在面向對象的理念看來,任何的編碼,都是由對象而來的,這裡也不例外。和之前介紹 WebDriver 時候的描述對應,我們需要用到兩種主要的類,並將其實例化。

    • WebDriver 類:主要靠直接實例化該類為對象,然後用其對象直接調用該類的方法和屬性
    • WebElement 類:主要通過 WebDriver 類實例化的對象,通過對頁面元素的查找,得到 WebElement 類的對象,然後調用該類的方法和屬性。

上述代碼中,使用了一個 WebDriver 類 的對象,即第2行,聲明了該類的對象,並賦值給變數 driver,接著變數 driver 作為 WebDriver 類的對象,使用了多個 WebDriver 類的方法。

注意:Chrome 是 WebDriver 的子類,是 WebDriver 類的一種

    • get(url): 第5行,打開網址
    • find_element_by_css_selector(selector): 第12、17、22行都使用了該方法,同時通過對該方法的調用,分別各產生了一個 WebElement類的對象,we_accountwe_password和最後一個匿名的對象,並通過產生的三個對象,調用 WebElement 類的方法
      • clear():清理頁面元素中的文字
      • send_keys(text):給頁面元素中,輸入新的文字
      • click():滑鼠左鍵點擊頁面元素

正是通過這樣的面向對象的方式,產生 Web司機(WebDriver類的對象),並且通過 Web司機不懈的努力,尋找到各種 Web元素(WebElement類的對象)進行操作,這樣便實現了 Selenium WebDriver 作為一款出色的瀏覽器測試工具,進行瀏覽器UI界面的自動化測試的代碼編寫和用例執行。

1.4 Selenium WebDriver API 的使用

通過上述最簡腳本的使用,我們可以來進一步了解 Selenium 的使用。事實上,上一節用的,便是 Selenium 的 WebDriver API。API(Application Programming Interface,應用程序編程介面,即通過編程語言,操作 WebDriver 的方法集合)

Selenium WebDriver API 官方參考:seleniumhq.github.io/se

具體API文檔地址:seleniumhq.github.io/se

  • API 使用: 用現成的類(大部分情況)的方法進行編程
    • WebDriver
    • WebElement
  • API 文檔
    • 編程使用說明
    • 介紹了每個方法的使用
      • 方法的作用
      • 方法的參數
      • 方法的返回值

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提供了一系列的定位符以便使用元素定位方法。常見的定位符有以下幾種:

  • id
  • name
  • class name
  • tag
  • link text
  • partial link text
  • xpath
  • css selector

那麼我們以下的操作將會基於上述的定位符進行定位操作。

對於元素的定位,WebDriver API可以通過定位簡單的元素和一組元素來操作。在這裡,我們需要告訴Selenium如何去找元素,以至於他可以充分的模擬用戶行為,或者通過查看元素的屬性和狀態,以便我們執行一系列的檢查。

在Selenium2中,WebDriver提供了多種多樣的find_element_by方法在一個網頁裡面查找元素。這些方法通過提供過濾標準來定位元素。當然WebDriver也提供了同樣多種多樣的find_elements_by的方式去定位多個元素。

儘管上述的方式,可以進行元素定位,實際上我們也是更多的用組合的方式進行元素定位。

方法Method描述Description參數Argument示例Exampleid該方法通過ID的屬性值去定位查找單個元素id: 需要被查找的元素的IDfind_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: 需要被查找的元素的xpathfind_element_by_xpath(//*[@id="xx"]/a)css_selector該方法通過CSS選擇器去定位查找單個元素css_selector: 需要被查找的元素的IDfind_element_by_css_selector(#search)

接下來的列表將會詳細展示find_elements_by的方法集合。這些方法依據匹配的具體標準返回一系列的元素。

方法Method描述Description參數Argument示例Exampleid該方法通過ID的屬性值去定位查找多個元素id: 需要被查找的元素的IDfind_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: 需要被查找的元素的xpathfind_elements_by_xpath("//div[contains(@class,list)]")css_selector該方法通過CSS選擇器去定位查找多個元素css_selector: 需要被查找的元素的IDfind_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 &amp; 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的內容,請查看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 &amp; 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:Python35Libsite-packagesseleniumwebdriversupportselect.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%按照手工的步驟進行操作。

比如步驟如下:

  1. 點擊一個 <a id="customer_chosen">
  2. 自動產生了一個 <ul id="customer_list">
  3. 點擊<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類中。

常用的滑鼠方法:

  • context_click() # 右擊
  • double_click() # 雙擊
  • drag_and_drop() # 拖拽
  • move_to_element() # 滑鼠停在一個元素上
  • click_and_hold() # 按下滑鼠左鍵在一個元素上

例子:

# 方法模擬滑鼠右鍵,參考代碼如下: # 引入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)

1.5 unittest 單元測試框

在上一節,我們對 Selenium WebDriver 的使用,僅僅停留在讓網頁自動的進行操作的階段,並沒有對任何一個步驟進行「檢查」。當然,這樣沒有「檢查」的操作,實際上是沒有測試意義的。那麼第一項,我們需要解決的便是「檢查」的問題。

所謂「檢查」,實際上就是斷言。對需要檢查的步驟操作,通過對預先設置的期望值,和執行結果的實際值之間的對比,得到測試的結果。在這裡,我們並不需要單獨的寫 if 語句進行各種判定,而是可以使用編程語言中對應的單元測試框架,即可解決好此類問題。

目前 Java 語言主流的單元測試框架有 JUnit 和 TestNG。Python 語言主流的單元測試框架有 unittest 。本小節的內容,主要介紹 unittest 的使用,探討單元測試框架如何幫助自動化測試。

接下來我們將會使用 Python 語言的unittest框架展開「檢查」。unittest框架的原本的名字是PyUnit。是從JUnit 這樣一個被廣泛使用的 經典的Java應用開發的單元測試框架創造而來。類似的框架還有NUnit(.Net開發的單元測試框架)等。我們可以使用unittest框架為任意Python項目編寫可理解的單元測試集合。現在這個unittest已經作為Python的標準庫模塊發布。我們安裝完Python以後,便可以直接使用unittest。

使用unittest需要以下簡單的三步:

  • 引入unittest模組
  • 繼承unittest.TestCase基類
  • 測試方法以test開頭

unittest 並未使用 Java 語言常見的註解方式,依舊停留在 比較早期的 Java 版本中依靠方法名稱進行識別的方式。主要有以下兩個固定名字的方法:

  • setUp():在每個測試方法運行前,執行。是測試前置條件。
  • tearDown():在每個測試方法運行後執行,是測試清理操作。

具體的代碼如下:

## 引入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)

需要注意步驟:

  1. 引入 unittest 模組
  2. 繼承 unittest.TestCase 類
  3. 做測試用例的方法,方法以 test_ 開頭
    1. 附加 setUp(), tearDown(), 在每個 test_ 方法執行前後 進行執行
      1. 附加 setUpClass(), tearDownClass()需要在 類實例化的對象,運行的開頭和結尾進行執行。加了星號(*)的步驟,可以不用。

      上述代碼運行結果如下:

      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
        • 清晰的單元測試框架,提供 TestCase, TestSuite, TextTestRunner 等基本類
        • unittest 是 原生 Python 的一部分
        • unittest 有第三方可用的 HTML 庫,可以輕鬆的生成 測試報告
      • 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可選,用來解釋失敗的原因

      1.6 為什麼需要封裝 Selenium

      • 什麼是封裝

      封裝是一個面向對象編程的概念,是面向對象編程的核心屬性,通過將代碼內部實現進行密封和包裝,從而簡化編程。對Selenium進行封裝的好處主要有如下三個方面:

        • 使用成本低
      1. 不需要要求所有的測試工程師會熟練使用Selenium,而只需要會使用封裝以後的代碼
      2. 不需要對所有的測試工程師進行完整培訓。也避免工作交接的成本。
      3. 測試人員使用統一的代碼庫
        • 維護成本低
      1. 通過封裝,在代碼發生大範圍變化和遷移的時候,不需要維護所有代碼,只需要變更封裝的部分即可
      2. 維護代碼不需要有大量的工程師,只需要有核心的工程師進行封裝的維護即可
        • 代碼安全性
      1. 對作為第三方的Selenium進行封裝,是代碼安全的基礎。
      2. 對於任何的代碼的安全隱患,必須由封裝來解決,使得風險可控。
      3. 使用者並不知道封裝內部的代碼結構。

      1.7 封裝的概念與基本操作

      • 關鍵方法的封裝思路

      封裝的具體示例:

        • 找到一個指定輸入框(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,並且通過index進行選擇

      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_...)
        • 支持各種的查找:8種方式都需要支持,必須通過 selector 顯示出分類
          • selector中需要包含一個特殊符號
          • 實例化 封裝好的類的時候,需要約定好是什麼特殊符號
      1. 強制性用硬編碼 hard code來實例化,例如 , 或者 ? 或者 其他非常用字元 =>
      2. 或者,構造方法中,傳遞 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

      1.8 Page-Object設計模式介紹

      • Page-Object設計模式的本質

      Page Object設計模式是Selenium自動化測試項目的最佳設計模式之一,強調測試、邏輯、數據和驅動相互分離。

      Page Object模式是Selenium中的一種測試設計模式,主要是將每一個頁面設計為一個Class,其中包含頁面中需要測試的元素(按鈕,輸入框,標題等),這樣在Selenium測試頁面中可以通過調用頁面類來獲取頁面元素,這樣巧妙的避免了當頁面元素id或者位置變化時,需要改測試頁面代碼的情況。當頁面元素id變化時,只需要更改測試頁Class中頁面的屬性即可。

      它的好處如下:

        • 集中管理元素對象,便於應對元素的變化
        • 集中管理一個page內的公共方法,便於測試用例的編寫
        • 後期維護方便,不需要重複的複製和修改代碼

      具體的做法如下:

        1. 創建一個頁面的類
        2. 在類的構造方法中,傳遞 WebDriver 參數。
        3. 在測試用例的類中,實例化頁面的類,並且傳遞在測試用例中已經實例化的WebDriver對象。
        4. 在頁面的類中,編寫該頁面的所有操作的方法
        5. 在測試用例的類中,調用這些方法
      • 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)
      1. 實例化Page

      self.main_page = MainPage(self.base_driver)

            1. 使用page的對象,調用成員方法

      self.main_page.open(self.base_url) self.main_page.change_language(lang)

      2. 構建測試方案

      2.1 數據驅動在自動化測試中的應用

      • 什麼是數據驅動

      主要的數據驅動方式有兩種:

        • 通過 文本文件或者 Excel 文件存儲數據,並通過程序讀取數據,遍歷所有的行
        • 通過資料庫存儲數據,並通過程序和 SQL 腳本讀取數據,遍歷所有的行

      通過 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()

      • 需要掌握的知識點:
      1. python的字典類型 dict 類型
      2. python的讀寫文件
      3. python的讀寫資料庫
      4. for循環
      5. 注意資源的釋放
        1. 關閉資料庫游標和連接
        2. 關閉文件

      2.2 測試方案的編碼實現

      • main.py 測試入口
      • runner.py 測試運行器
      • cases 測試用例
      • pages 測試頁面
      • base 底層封裝與驅動

      2.3 測試報告的生成

      • 如何生成測試報告
      • 測試報告的種類
      • 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()

      2.4 具體案例分析

      • Page-Object 的設計架構
      • Page 的分割
      • BDD Behave 框架對自動化測試的作用
      • 在自動化測試中 驗證 資料庫
      • 在自動化測試中 驗證 Web 介面
      • 自動化測試的優秀實踐總結
      • BA 與 QA 人員在自動化測試中的分工與合作

      自動化測試在路上

      • [探討]單元測試在敏捷開發的場景下對技術和產品的影響?
        • 技術人員需要掌握的技術棧
        • 企業產品的質量度量與提升

      推薦閱讀:

      TAG:Selenium | 自動化測試 | Selenium2自動化測試實戰基於Python語言書籍 |