基于 SELENIUM 的自動化測試架構(gòu)
非常感謝各位查閱本篇文章,筆者在此感謝各位蠕嫁。
目前市面上有分門別類的自動化測試工具,這篇文章將討論開源自動化測試工具 Selenium
的使用鳄炉,以及圍繞該工具進(jìn)行自動化測試的理念阐虚、方案以及測試架構(gòu)的構(gòu)建。
1. 工具的使用
1.1 Selenium 介紹
Selenium
是開源的自動化測試工具启摄,它主要是用于Web 應(yīng)用程序的自動化測試稿壁,不只局限于此,同時支持所有基于web 的管理任務(wù)自動化歉备。
Selenium
官網(wǎng)的介紹如下:
Selenium is a suite of tools to automate web browsers across many platforms.
- runs in many browsers and operatingsystems
- can be controlled by many programming languages and testing frameworks.
Selenium 是用于測試 Web 應(yīng)用程序用戶界面 (UI) 的常用框架傅是。它是一款用于運(yùn)行端到端功能測試的超強(qiáng)工具。您可以使用多個編程語言編寫測試,并且 Selenium 能夠在一個或多個瀏覽器中執(zhí)行這些測試喧笔。
Selenium 經(jīng)歷了三個版本:Selenium 1帽驯,Selenium 2 和 Selenium 3。Selenium 也不是簡單一個工具书闸,而是由幾個工具組成尼变,每個工具都有其特點(diǎn)和應(yīng)用場景。
Selenium 誕生于 2004 年浆劲,當(dāng)在 ThoughtWorks 工作的 Jason Huggins 在測試一個內(nèi)部應(yīng)用時嫌术。作為一個聰明的家伙,他意識到相對于每次改動都需要手工進(jìn)行測試牌借,他的時間應(yīng)該用得更有價值度气。他開發(fā)了一個可以驅(qū)動頁面進(jìn)行交互的 Javascript 庫,能讓多瀏覽器自動返回測試結(jié)果膨报。那個庫最終變成了 Selenium 的核心磷籍,它是 Selenium RC(遠(yuǎn)程控制)和 Selenium IDE 所有功能的基礎(chǔ)。Selenium RC 是開拓性的丙躏,因?yàn)闆]有其他產(chǎn)品能讓你使用自己喜歡的語言來控制瀏覽器择示。這就是 Selenium 1。
然而晒旅,由于它使用了基于 Javascript 的自動化引擎栅盲,而瀏覽器對 Javascript 又有很多安全限制,有些事情就難以實(shí)現(xiàn)废恋。更糟糕的是谈秫,網(wǎng)站應(yīng)用正變得越來越強(qiáng)大,它們使用了新瀏覽器提供的各種特性鱼鼓,都使得這些限制讓人痛苦不堪拟烫。
在 2006 年,一名 Google 的工程師迄本, Simon Stewart 開始基于這個項(xiàng)目進(jìn)行開發(fā)硕淑,這個項(xiàng)目被命名為 WebDriver。此時嘉赎,Google 早已是 Selenium 的重度用戶置媳,但是測試工程師們不得不繞過它的限制進(jìn)行工具。Simon 需要一款能通過瀏覽器和操作系統(tǒng)的本地方法直接和瀏覽器進(jìn)行通話的測試工具公条,來解決Javascript 環(huán)境沙箱的問題拇囊。WebDriver 項(xiàng)目的目標(biāo)就是要解決 Selenium 的痛點(diǎn)。
到了 2008 年靶橱,Selenium 和 WebDriver 兩個項(xiàng)目合并寥袭。Selenium 有著豐富的社區(qū)和商業(yè)支持路捧,但 WebDriver 顯然代表著未來的趨勢。兩者的合并為所有用戶提供了一組通用功能传黄,并且借鑒了一些測試自動化領(lǐng)域最閃光的思想杰扫。這就是 Selenium 2。
2016 年膘掰,Selenium 3 誕生涉波。移除了不再使用的 Selenium 1 中的 Selenium RC,并且官方重寫了所有的瀏覽器驅(qū)動炭序。
1.2 Selenium 工具集
-
Selenium IDE
Selenium IDE (集成開發(fā)環(huán)境) 是一個創(chuàng)建測試腳本的原型工具啤覆。它是一個 Firefox 插件,實(shí)現(xiàn)簡單的瀏覽器操作的錄制與回放功能惭聂,提供創(chuàng)建自動化測試的建議接口窗声。Selenium IDE 有一個記錄功能,能記錄用戶的操作辜纲,并且能選擇多種語言把它們導(dǎo)出到一個可重用的腳本中用于后續(xù)執(zhí)行笨觅。
-
Selenium RC
Selenium RC 是selenium 家族的核心工具,Selenium RC 支持多種不同的語言編寫自動化測試腳本耕腾,通過selenium RC 的服務(wù)器作為代理服務(wù)器去訪問應(yīng)用從而達(dá)到測試的目的见剩。
selenium RC 使用分Client Libraries 和Selenium Server。
- Client Libraries 庫主要主要用于編寫測試腳本扫俺,用來控制selenium Server 的庫苍苞。
- Selenium Server 負(fù)責(zé)控制瀏覽器行為,總的來說狼纬,Selenium Server 主要包括3 個部分:Launcher羹呵、Http Proxy、Core疗琉。
-
Selenium Grid
Selenium Grid 使得 Selenium RC 解決方案能提升針對大型的測試套件或者哪些需要運(yùn)行在多環(huán)境的測試套件的處理能力冈欢。Selenium Grid 能讓你并行的運(yùn)行你的測試,也就是說盈简,不同的測試可以同時跑在不同的遠(yuǎn)程機(jī)器上凑耻。這樣做有兩個有事,首先柠贤,如果你有一個大型的測試套件香浩,或者一個跑的很慢的測試套件,你可以使用 Selenium Grid 將你的測試套件劃分成幾份同時在幾個不同的機(jī)器上運(yùn)行种吸,這樣能顯著的提升它的性能弃衍。同時呀非,如果你必須在多環(huán)境中運(yùn)行你的測試套件坚俗,你可以獲得多個遠(yuǎn)程機(jī)器的支持镜盯,它們將同時運(yùn)行你的測試套件。在每種情況下猖败,Selenium Grid 都能通過并行處理顯著地縮短你的測試套件的處理時間速缆。
-
Selenium WebDriver
WebDriver 是 Selenium 2 主推的工具,事實(shí)上WebDriver是Selenium RC的替代品恩闻,因?yàn)镾elenium需要保留向下兼容性的原因艺糜,在 Selenium 2 中, Selenium RC才沒有被徹底的拋棄幢尚,如果使用Selenium開發(fā)一個新的自動化測試項(xiàng)目破停,那么我們強(qiáng)烈推薦使用Selenium2 的 WebDriver進(jìn)行編碼。另外尉剩, 在Selenium 3 中真慢,Selenium RC 被移除了。
1.3 Selenium WebDriver 的使用
接下來的內(nèi)容理茎,我們將會主要討論本文的核心重點(diǎn)黑界, Selenium WebDriver 的使用。 Selenium WebDriver 是從 Selenium 2 開始使用并流行皂林, 在 Selenium 3 中得到進(jìn)一步發(fā)展的工具朗鸠,是當(dāng)前 Selenium 的最核心的工具础倍。WebDriver 具有清晰面向?qū)ο?API,能以最佳的方式與瀏覽器進(jìn)行交互。
Selenium WebDriver 就好比是一個懂瀏覽器的司機(jī)蹬竖,它可以在瀏覽器的網(wǎng)頁上行走店乐,走到網(wǎng)頁內(nèi)容的任何地方段誊,可以參觀網(wǎng)頁的任何地方,并且和網(wǎng)頁進(jìn)行交互栈拖。那么作為測試工程師枕扫,如果想和這樣的一個司機(jī)打交道,就必須要掌握和這樣的司機(jī)打交道的技能辱魁。
- 學(xué)習(xí)司機(jī)會使用的語言烟瞧,并使用該語言,以及合適的溝通工具與司機(jī)進(jìn)行交流
- Java
- Python
- C#
- JavaScript
- PHP
- Ruby
- 給司機(jī)找到合適的瀏覽器染簇,以便司機(jī)在瀏覽器上行走参滴。
- 支持多種瀏覽器,包括 Chrome锻弓,F(xiàn)irefox砾赔,IE,Edge青灼,Safari暴心,Opera 等
Selenium WebDriver 的使用主要分為兩個場景:
- 懂瀏覽器的司機(jī),WebDriver 類
- 用 WebDriver 提供的模板杂拨,制造一個司機(jī)专普。
- WebDriver 的第一個應(yīng)用場景,就是這個司機(jī)的各種能力弹沽,包括但不限于以下的部分
- 用瀏覽器打開指定的 URL
- 清理瀏覽器的Cookie
- 在瀏覽器中尋找頁面元素(Web Element)
- 查找單個的指定元素
- 查找一組有共同屬性的元素檀夹,并進(jìn)行遍歷等。
- 控制瀏覽器的基本操作:
- 前進(jìn): forward()
- 后退: backward()
- 刷新: refresh()
- 關(guān)閉: close()
- 最大化: maximize_window()
- 彈窗: switch_to_alert()
- 返回瀏覽器的屬性
- current_url
- title
- 執(zhí)行 JavaScript 腳本
- 在瀏覽器中找到的元素策橘,WebElement 類
- 司機(jī)在瀏覽器中找到頁面元素以后炸渡,對它做的任何操作,都是 WebDriver 的第二個主要的場景
- 點(diǎn)擊該元素: click()
- 清除該元素原有的文字: clear()
- 給該元素填寫新的文字: send_keys()
- 獲取該元素的文字: text
- 獲取該元素的指定屬性: get_attribute()
- 對該元素進(jìn)行二次加工
- 構(gòu)成 frame 并切換進(jìn)去: switch_to.frame(元素)
- 構(gòu)成 select 并進(jìn)行操作: Select(元素).select_by_value()
- 司機(jī)在瀏覽器中找到頁面元素以后炸渡,對它做的任何操作,都是 WebDriver 的第二個主要的場景
1.4 Selenium 環(huán)境搭建
Selenium 的環(huán)境搭建基本上分為三個部分:
- 安裝編程語言以及IDE(集成編程環(huán)境)丽已,用來操作 WebDriver
- 安裝 Selenium WebDriver蚌堵,實(shí)現(xiàn)瀏覽器的測試
- 安裝瀏覽器,和指定的驅(qū)動,完成自動化測試的執(zhí)行
接下來分別用目前市面上主流的 Java 和 Python 環(huán)境進(jìn)行搭建吼畏。
-
Java 版本
安裝 Java 語言督赤,即 JDK。推薦 1.8 的版本宫仗。
安裝 IDE,推薦 JetBrains IDEA Community Edition旁仿,這款是目前主流的 Java 開發(fā)工具藕夫,而且社區(qū)版是免費(fèi)使用的,擁有出色的用戶交互枯冈,以及使用界面毅贮,完全能夠應(yīng)對一般的自動化測試的編程。當(dāng)然如果你更加熟悉Eclipse尘奏,也是可以使用的滩褥。
-
安裝 Selenium,推薦使用 Maven 直接引入依賴炫加。當(dāng)自動化測試作為團(tuán)隊(duì)共同的解決方案瑰煎,而不是一個人單獨(dú)維護(hù)的方案的時候,團(tuán)隊(duì)需要統(tǒng)一的 Selenium 版本以及共同的 API 操作俗孝,Maven 的使用酒甸,無疑簡化了項(xiàng)目的難度,很好的解決了此類問題赋铝。
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>3.3.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-remote-driver --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-remote-driver</artifactId> <version>3.3.1</version> </dependency>
當(dāng)然插勤,你也可以直接下載 Selenium Standalone Server,并且手工引用
Jar 包
到項(xiàng)目中革骨。最新版3.3.1的下載地址:3.3.1 安裝 瀏覽器和瀏覽器的驅(qū)動农尖。
以上的步驟,便完成了 Java + Selenium 的環(huán)境搭建良哲。
-
Python 版本
-
安裝 Python 語言盛卡。
Python 目前并行了兩套版本,2.x 和 3.x筑凫。如果你之前沒有 Python 的使用經(jīng)驗(yàn)窟扑,建議使用 Python 3.x 版本。兩套版本互相不兼容漏健,并且 Python 從 3.5(含)開始嚎货,不再支持 Windows XP 系統(tǒng),請注意蔫浆。
安裝 Python IDE殖属,推薦 JetBrains Pycharm Community Edition。
-
安裝 Selenium瓦盛,推薦使用 pip 的方式直接安裝洗显。在命令行下外潜,直接輸入:
# 在Selenium 3 發(fā)布之前,可以用下面命令直接裝selenium # -U = --upgrade 升級安裝 # 自動安裝最新版 # 目前3.0發(fā)布以后挠唆,這個命令直接安裝 3.3.1 的最新版 pip install -U selenium # 如果要裝2.53.6版本 pip install selenium==2.53.6
如果你處于沒有外網(wǎng)的情況下处窥,可以采用源碼解壓安裝,前往https://pypi.python.org/pypi/selenium下載最新版的PyPI版本的Selenium玄组,解壓后執(zhí)行:
python setup.py install
-
安裝 瀏覽器和瀏覽器的驅(qū)動滔驾。
以上的步驟,便完成了 Python + Selenium 的環(huán)境搭建俄讹。
-
1.5 Selenium 編程
通過前面的介紹哆致,我們知道 Selenium 支持多種語言,并且推薦使用面向?qū)ο蟮姆绞竭M(jìn)行編程患膛。接下來我們將著重介紹如何使用面向?qū)ο蟮姆绞竭M(jìn)行編程摊阀。
在面向?qū)ο蟮睦砟羁磥恚魏蔚木幋a踪蹬,都是由對象而來的胞此,這里也不例外。和之前介紹 WebDriver 時候的描述對應(yīng)跃捣,我們需要用到兩種主要的類豌鹤,并將其實(shí)例化。
- WebDriver 類:主要靠直接實(shí)例化該類為對象枝缔,然后用其對象直接調(diào)用該類的方法和屬性
- WebElement 類:主要通過 WebDriver 類實(shí)例化的對象布疙,通過對頁面元素的查找,得到 WebElement 類的對象愿卸,然后調(diào)用該類的方法和屬性灵临。
具體的使用如下,以 Java 語言 和 火狐瀏覽器為例
// 聲明 Web司機(jī)趴荸,司機(jī)是一個火狐類的對象
// 需要用 new 關(guān)鍵字來實(shí)例化對象儒溉, () 代表構(gòu)造方法
WebDriver driver = new FirefoxDriver();
// Web司機(jī)去打開網(wǎng)站
driver.get("http://demo.ranzhi.org");
// 線程停止 3000 毫秒,使得 Web司機(jī)有足夠的時間打開網(wǎng)址
Thread.sleep(3000);
// 選擇 用戶名 密碼 并依次輸入 demo 和 demo (用戶名和密碼都是 demo)
weAccount = driver.findElement(By.cssSelector("#account"));
weAccount.clear();
weAccount.sendKeys("demo");
wePassword = driver.findElement(By.cssSelector("#password"));
wePassword.clear();
wePassword.sendKeys("demo");
// 選擇 登錄 按鈕发钝,并點(diǎn)擊 click
driver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(5000);
上述代碼中顿涣,使用了一個 WebDriver 類 的對象,即第3行酝豪,聲明了該類的對象涛碑,并賦值給變量 driver,接著變量 driver 作為 WebDriver 類的對象孵淘,使用了多個 WebDriver 類的方法蒲障。
- get(url): 第6行,打開網(wǎng)址
- findElement(by, selector): 第12、16揉阎、21行都使用了該方法庄撮,同時通過對該方法的調(diào)用,分別各產(chǎn)生了一個 WebElement類的對象毙籽,
weAccount
洞斯,wePassword
和最后一個匿名的對象,并通過產(chǎn)生的三個對象坑赡,調(diào)用 WebElement 類的方法- clear():清理頁面元素中的文字
- sendKeys(text):給頁面元素中烙如,輸入新的文字
- click():鼠標(biāo)左鍵點(diǎn)擊頁面元素
正是通過這樣的面向?qū)ο蟮姆绞剑a(chǎn)生 Web司機(jī)(WebDriver類的對象)垮衷,并且通過 Web司機(jī)不懈的努力厅翔,尋找到各種 Web元素(WebElement類的對象)進(jìn)行操作乖坠,這樣便實(shí)現(xiàn)了 Selenium WebDriver 作為一款出色的瀏覽器測試工具搀突,進(jìn)行瀏覽器UI界面的自動化測試的代碼編寫和用例執(zhí)行。
上述代碼熊泵,也同樣可是使用 Python 作為編程語言進(jìn)行操作仰迁,如下所示:
# 聲明一個司機(jī),司機(jī)是個Firefox類的對象
driver = webdriver.Firefox()
# 讓司機(jī)加載一個網(wǎng)頁
driver.get("http://demo.ranzhi.org")
# 給司機(jī)3秒鐘去打開
sleep(3)
# 開始登錄
# 1. 讓司機(jī)找用戶名的輸入框
we_account = driver.find_element_by_css_selector('#account')
we_account.clear()
we_account.send_keys("demo")
# 2. 讓司機(jī)找密碼的輸入框
we_password = driver.find_element_by_css_selector('#password')
we_password.clear()
we_password.send_keys("demo")
# 3. 讓司機(jī)找 登錄按鈕 并 單擊
driver.find_element_by_css_selector('#submit').click()
sleep(3)
常用的重點(diǎn)編程對象有如下幾種:
-
WebDriver 類
- get(url): 打開web頁面
- findElement(by, selector): 查找一個頁面元素
- 配合瀏覽器的開發(fā)者工具(推薦 Chrome Developer Tools)顽分,有8中方式定位元素:
- id:元素標(biāo)簽的 id
- css selector:元素標(biāo)簽的 selector
- xpath:元素標(biāo)簽的 XPath
- link text:元素標(biāo)簽的完整文字
- name:元素標(biāo)簽的 name
- class name:元素標(biāo)簽的 class name
- tag name:元素標(biāo)簽的 tag name
- partial link text:元素標(biāo)簽的部分文字
- 配合瀏覽器的開發(fā)者工具(推薦 Chrome Developer Tools)顽分,有8中方式定位元素:
findElements(by, selector):查找一組具有同一屬性的頁面元素徐许,方式同上。
deleteAllCookies():清理 Cookies
executeJs(js):執(zhí)行 JavaScript
quit(): 退出瀏覽器
getTitle():當(dāng)前瀏覽器的標(biāo)題屬性
getCurrentUrl():獲取當(dāng)前瀏覽器的 URL
-
WebElement 類
click():點(diǎn)擊改元素
clear():清除當(dāng)前的文本
sendKeys(text):給當(dāng)前的元素輸入文字
getAttribute(attribute):獲取當(dāng)前元素的某一種屬性
getText():獲取當(dāng)前元素的文字屬性
isDisplayed():獲取當(dāng)前元素的 Displayed 屬性
-
Select:針對
<select>
元素進(jìn)行的操作selectByValue(value)
-
selectByIndex(index)
// 找到該 <select> 元素 we WebElement we = driver.findElement(by, selector); // 使用該元素卒蘸,實(shí)例化一個 Select 類的對象 s Select s = new Select(we); s.selectByValue(value); // 或者用index s.selectByIndex(value)
-
鼠標(biāo)事件雌隅,有關(guān)鼠標(biāo)的操作,不只是單擊缸沃,有時候還要做右擊恰起、雙擊、拖動等操作趾牧。這些操作包含在ActionChains類中检盼。
- contextClick():右擊
- douchClick():雙擊
- dragAndDrop():拖拽
- moveToElement():鼠標(biāo)停在一個元素上
- clickAndHold():按下鼠標(biāo)左鍵在一個元素上
-
Frame: 針對
<iframe>
元素標(biāo)簽進(jìn)行的操作很多的頁面中,都包含有內(nèi)聯(lián)框架(iframe)翘单,那么如果需要獲取到其內(nèi)部的元素并進(jìn)行操作吨枉,必須首先切換到該內(nèi)聯(lián)框架中,當(dāng)操作完成以后哄芜,再退出到最外層的網(wǎng)頁中
// 找到該內(nèi)聯(lián)框架的元素 WebElement we = driver.findElement(by, selector); // 利用WebDriver 的對象driver貌亭,切換到該內(nèi)聯(lián)框架中 driver.switchTo().frame(we); // TODO: 進(jìn)行各種操作 // 退出該內(nèi)聯(lián)框架,返回到外層的網(wǎng)頁中 driver.switchTo().defaultContent();
?
2. 理念與方案
在第一部分认臊,工具的使用中属提,我們重點(diǎn)介紹了 Selenium 工具的編程,但是這樣其實(shí)對于自動化測試來講,還遠(yuǎn)遠(yuǎn)不夠冤议。自動化測試的重點(diǎn)斟薇,其實(shí)依舊是測試用例的編寫和執(zhí)行,要求代碼中恕酸,具備測試用例的屬性堪滨;同時要求測試的代碼能夠很好的組織起來,通過抽取和分離的理念蕊温,實(shí)現(xiàn)良好的測試袱箱。主要達(dá)到以下的幾個目的:
-
具備測試用例的屬性
測試代碼,可以輕松的具備測試用例的屬性义矛,主要包括測試前置條件发笔、清理操作、和斷言(檢查)凉翻。
-
避免重復(fù)代碼的編寫和復(fù)制
通過模塊化拆分頁面功能了讨,避免 WebDriver類的重復(fù)實(shí)例化和調(diào)用,也避免同樣的測試步驟制轰,多次的編寫和復(fù)制
-
測試數(shù)據(jù)單獨(dú)存放
測試代碼中不需要包含需要輸入的測試數(shù)據(jù)前计,而是把測試數(shù)據(jù)單獨(dú)存放在 文本文件,或者數(shù)據(jù)庫中垃杖。
-
封裝底層的測試工具
對 Selenium WebDriver 這種第三方的工具男杈,進(jìn)行封裝起來,避免代碼中直接調(diào)用
-
必須使用源代碼管理工具
無論是否是
一人團(tuán)隊(duì)
调俘,源代碼管理工具的使用都是積極地和必要的伶棒,推薦使用 Git 。
接下來的描述彩库,將會對上述的理念依次進(jìn)行講解肤无,實(shí)現(xiàn)自動化測試的方案。
2.1 使用單元測試框架
在第一部分侧巨,我們對 Selenium WebDriver 的使用凛澎,僅僅停留在讓網(wǎng)頁自動的進(jìn)行操作的階段库糠,并沒有對任何一個步驟進(jìn)行“檢查”疲酌。當(dāng)然锄蹂,這樣沒有“檢查”的操作,實(shí)際上是沒有測試意義的坦仍。那么第一項(xiàng)鳍烁,我們需要解決的便是“檢查”的問題。
所謂“檢查”繁扎,實(shí)際上就是斷言幔荒。對需要檢查的步驟操作糊闽,通過對預(yù)先設(shè)置的期望值,和執(zhí)行結(jié)果的實(shí)際值之間的對比爹梁,得到測試的結(jié)果右犹。在這里,我們并不需要單獨(dú)的寫 if
語句進(jìn)行各種判定姚垃,而是可以使用編程語言中對應(yīng)的單元測試框架念链,即可解決好此類問題。
目前 Java 語言主流的單元測試框架有 JUnit 和 TestNG积糯。Python 語言主流的單元測試框架有 unittest 掂墓。本小節(jié)的內(nèi)容,主要介紹 TestNG 和 unittest 的使用看成,探討單元測試框架如何幫助自動化測試君编。
-
TestNG
接下來我們將會使用Java語言的
TestNG
框架展開“檢查”。TestNG為我們在項(xiàng)目測試中常用到的單元測試框架川慌,很多程序員的理想套件吃嘿,通過注解(annotation)的方式進(jìn)行操作。在
TestNG
提供了@BeforeMethod
和@AfterMethod
窘游,在每個測試函數(shù)調(diào)用之前/后都會調(diào)用唠椭。-
@BeforeMethod
: Method annotated with@BeforeMethod
executes before every test method. -
@AfterMethod
: Method annotated with@AfterMethod
executes after every test method.
如果在測試之前有些工作我們只想做一次跳纳,用不著每個函數(shù)之前都做一次忍饰,那就用下面兩個來標(biāo)注:
-
@BeforeTest
: 在第一個 test method 開始執(zhí)行前,執(zhí)行寺庄。 -
@AfterTest
: 在最后一個 test method 執(zhí)行后再執(zhí)行艾蓝。
接下來我們用具體的代碼示例,解釋單元測試框架的使用
-
TestNG 框架圖
TestNG單元測試框架示意圖.png -
TestNG 斷言
方法 Method 檢查條件 assertEquals(a, b [, msg])
a == b斗塘,msg可選赢织,用來解釋失敗的原因 assertNotEquals(a, b [, msg]
a != b,msg可選馍盟,用來解釋失敗的原因 assertTrue(x [, msg])
x 是真于置,msg可選,用來解釋失敗的原因 assertFalse(x [, msg])
x 是假贞岭,msg可選八毯,用來解釋失敗的原因 assertIsNot(a, b [, msg])
a 不是 b,msg可選瞄桨,用來解釋失敗的原因 assertNull(x[, msg])
x 是null话速,msg可選,用來解釋失敗的原因 assertNotNull(x[, msg])
x 不是null芯侥,msg可選泊交,用來解釋失敗的原因 -
TestNG 的引入
這里我們依舊使用 Maven 的方式乳讥,引入 TestNG 到項(xiàng)目中。
<dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.8</version> </dependency>
?
-
TestNG 的使用
/** * Created by Linty on 1/8/2017. * 使用 @BeforeTest 和 @AfterTest 進(jìn)行測試框架操作 * 如果直接運(yùn)行整個測試廓俭,運(yùn)行步驟如下 * 首先運(yùn)行 @BeforeTest * 然后運(yùn)行 test01ChangeLanguage * 接著運(yùn)行 test02LogIn * 最后運(yùn)行 @AfterTest */ public class RanzhiMainTest { // 聲明兩個全局變量云石,每個方法都可以用下面的變量。 private WebDriver baseDriver = null; private String baseUrl = null; /** * 測試登錄 * 需要用 @Test 注解進(jìn)行標(biāo)注 * 這樣 不用main()方法 便可直接運(yùn)行測試 * * @throws InterruptedException */ @Test public void test02LogIn() throws InterruptedException { WebDriver driver = this.baseDriver; driver.get(this.baseUrl); Thread.sleep(2000); driver.findElement(By.id("account")).sendKeys("admin"); driver.findElement(By.id("password")).sendKeys("123456"); driver.findElement(By.id("submit")).click(); // 點(diǎn)擊登錄按鈕后研乒,需要等待瀏覽器刷新 Thread.sleep(2000); String expectedUrl = this.baseUrl + "sys/index.html"; // driver.getCurrentUrl() -- 獲取當(dāng)前的瀏覽器URL Assert.assertEquals(driver.getCurrentUrl(), expectedUrl); } /** * 測試切換語言 * 把系統(tǒng)語言切換成 English * 然后查詢 語言的按鈕 是不是變成了 English * @throws InterruptedException */ @Test public void test01ChangeLanguage() throws InterruptedException { WebDriver driver = this.baseDriver; driver.get(this.baseUrl); Thread.sleep(2000); // 點(diǎn)擊語言按鈕 driver.findElement(By.cssSelector("#langs > button")).click(); Thread.sleep(500); // 用Css Selector 選擇 英文 driver.findElement(By.cssSelector("#langs > ul > li:nth-child(3) > a")).click(); // 瀏覽器需要刷新留晚,等待2秒鐘 Thread.sleep(2000); // 檢查按鈕上的字是不是變成了 English String expected_language = "English"; String actual_language = driver.findElement(By.cssSelector("#langs > button")).getText(); Assert.assertEquals(actual_language, expected_language); } /** * 測試前置條件 * 在所有的測試開始前 執(zhí)行一次 */ @BeforeTest public void setUp() { this.baseDriver = new FirefoxDriver(); this.baseUrl = "http://demo.ranzhi.org/"; } /** * 測試清理操作 * 在所有的測試結(jié)束后 執(zhí)行一次 */ @AfterTest public void tearDown() { this.baseDriver.quit(); } }
?
-
-
unittest
接下來我們將會使用 Python 語言的
unittest
框架展開“檢查”。unittest
框架的原本的名字是PyUnit告嘲。是從JUnit 這樣一個被廣泛使用的 經(jīng)典的Java應(yīng)用開發(fā)的單元測試框架創(chuàng)造而來错维。類似的框架還有NUnit(.Net開發(fā)的單元測試框架)等。我們可以使用unittest框架為任意Python項(xiàng)目編寫可理解的單元測試集合¢匣#現(xiàn)在這個unittest已經(jīng)作為Python的標(biāo)準(zhǔn)庫模塊發(fā)布赋焕。我們安裝完P(guān)ython以后,便可以直接使用unittest仰楚。使用unittest需要以下簡單的三步:
- 引入unittest模組
- 繼承unittest.TestCase基類
- 測試方法以
test
開頭
unittest 并未使用 Java 語言常見的注解方式隆判,依舊停留在 比較早期的 Java 版本中依靠方法名稱進(jìn)行識別的方式。主要有以下兩個固定名字的方法:
- setUp():在每個測試方法運(yùn)行前僧界,執(zhí)行侨嘀。是測試前置條件。
- tearDown():在每個測試方法運(yùn)行后執(zhí)行捂襟,是測試清理操作咬腕。
具體的代碼如下:
class RanzhiMainTest(unittest.TestCase): """ 第一步:import unittest 第二步:繼承 unittest.TestCase 類 第三步:測試的方法,以test_ 開頭 第四步:重寫 setUp() 作為 測試前置條件葬荷,注意setUp的大小寫涨共,必須一致 第五步:重寫 tearDown() 作為 測試清理操作,注意 tearDown的大小寫宠漩,必須一致 """ # 全局變量 base_driver = None base_url = None def setUp(self): self.base_driver = webdriver.Firefox() self.base_url = "http://demo.ranzhi.org/" def tearDown(self): self.base_driver.quit() def test_01_change_language(self): driver = self.base_driver driver.get(self.base_url) sleep(2) driver.find_element_by_css_selector("#langs > button").click() sleep(1) driver.find_element_by_css_selector("#langs > ul > li:nth-child(3) > a").click() sleep(2) # 頁面應(yīng)該換成英語了 actual_lang = driver.find_element_by_css_selector("#langs > button").text expected_lang = "English" # 與Java的TestNG 相反举反,先寫期待值,再寫實(shí)際值 self.assertEqual(expected_lang, actual_lang) def test_02_log_in(self): driver = self.base_driver driver.get(self.base_url) sleep(2) driver.find_element_by_id("account").send_keys("admin") driver.find_element_by_id("password").send_keys("123456") driver.find_element_by_id("submit").click() sleep(3) actual_url = driver.current_url expected_url = self.base_url + "sys/index.html" self.assertEqual(expected_url, actual_url)
?
2.2 使用 Page Object 設(shè)計(jì)模式
Page Object設(shè)計(jì)模式是Selenium自動化測試項(xiàng)目的最佳設(shè)計(jì)模式之一扒吁,強(qiáng)調(diào)測試火鼻、邏輯、數(shù)據(jù)和驅(qū)動相互分離雕崩。
Page Object模式是Selenium中的一種測試設(shè)計(jì)模式魁索,主要是將每一個頁面設(shè)計(jì)為一個Class,其中包含頁面中需要測試的元素(按鈕晨逝,輸入框蛾默,標(biāo)題等),這樣在Selenium測試頁面中可以通過調(diào)用頁面類來獲取頁面元素捉貌,這樣巧妙的避免了當(dāng)頁面元素id或者位置變化時支鸡,需要改測試頁面代碼的情況冬念。當(dāng)頁面元素id變化時,只需要更改測試頁Class中頁面的屬性即可牧挣。
它的好處如下:
- 集中管理元素對象急前,便于應(yīng)對元素的變化
- 集中管理一個page內(nèi)的公共方法,便于測試用例的編寫
- 后期維護(hù)方便瀑构,不需要重復(fù)的復(fù)制和修改代碼
具體的做法如下:
- 創(chuàng)建一個頁面的類
- 在類的構(gòu)造方法中裆针,傳遞 WebDriver 參數(shù)。
- 在測試用例的類中寺晌,實(shí)例化頁面的類世吨,并且傳遞在測試用例中已經(jīng)實(shí)例化的WebDriver對象。
- 在頁面的類中呻征,編寫該頁面的所有操作的方法
- 在測試用例的類中耘婚,調(diào)用這些方法
實(shí)現(xiàn)的示例
-
Page 基類
設(shè)計(jì)了一個基本的Page類,以便所有的頁面進(jìn)行繼承陆赋,該類標(biāo)明了一個sub page類的基本功能和公共的功能沐祷。
-
全局變量: this.baseDriver,讓所有的子類都使用的攒岛。
// 基類的變量赖临,所有繼承的類,都可以使用 BoxDriver baseDriver;
-
構(gòu)造方法:
-
默認(rèn)的構(gòu)造方法灾锯,無參數(shù)的構(gòu)造方法
public BasePage() { }
-
傳遞 driver的構(gòu)造方法
public BasePage(BoxDriver driver) { this.baseDriver = driver; }
-
-
私有的常量:存放元素的定位符
private String START_BUTTON_SELECTOR = "s,#start > div"; private final String EXIT_MENU_TEXT = "l,%s";
-
成員方法:
-
每個子類都需要的系統(tǒng)功能:
-
open
public void open(String url) throws InterruptedException { this.baseDriver.navigate(url); Thread.sleep(2000); }
-
-
所有子類(頁面)都具有的業(yè)務(wù)功能
- selectApp
- logout
-
-
Sub Pages(s)子類
具體的頁面的類兢榨,定義了某個具體的頁面的功能
-
必須繼承基類
public class AdminPage extends BasePage { }
-
創(chuàng)建構(gòu)造方法,帶driver 參數(shù)
public AdminPage(BoxDriver driver) { super(driver); }
特定頁面的業(yè)務(wù)
使用基類的
this.baseDriver
成員變量
-
Tests 類
這部分描述的是具體的測試用例挠进。
-
聲明全局變量
private BoxDriver baseDriver = null; private String baseUrl = null; private LoginPage loginPage = null; private AdminPage adminPage = null;
-
調(diào)用各種頁面(pages)
-
實(shí)例化Page
this.loginPage = new LoginPage(this.baseDriver); this.adminPage = new AdminPage(this.baseDriver);
-
使用page的對象色乾,調(diào)用成員方法
loginPage.open(this.baseUrl); loginPage.changeLanguage(lang); loginPage.login("admin", "123456", true); loginPage.selectApp(AppType.Admin); adminPage.clickAddMemberButton(); adminPage.addMemberData(member);
-
2.3 使用數(shù)據(jù)驅(qū)動
主要的數(shù)據(jù)驅(qū)動方式有兩種:
- 通過 文本文件或者 Excel 文件存儲數(shù)據(jù)誊册,并通過程序讀取數(shù)據(jù)领突,遍歷所有的行
- 通過數(shù)據(jù)庫存儲數(shù)據(jù),并通過程序和 SQL 腳本讀取數(shù)據(jù)案怯,遍歷所有的行
通過 CSV 文件 或者 MySQL 數(shù)據(jù)庫君旦,是主流的數(shù)據(jù)驅(qū)動方式。當(dāng)然數(shù)據(jù)驅(qū)動也可以結(jié)合單元測試框架的參數(shù)化測試進(jìn)行編寫(此部分本文不做具體描述)嘲碱。
無論使用了 哪一種(CSV 或者 MySQL)金砍,讀取數(shù)據(jù)后都要進(jìn)行遍歷操作。
-
Java 代碼
// 布爾型 true false boolean isFirstLine = true; // 循環(huán)每一個行麦锯,接下來根據(jù)每一行的值(數(shù)據(jù))恕稠,進(jìn)行測試 for (CSVRecord row : csvData) { if (isFirstLine) { isFirstLine = false; continue; // continue的作用 // 當(dāng)前循環(huán)到此為止,直接進(jìn)入下一條循環(huán) } Member member = new Member(); member.setAccount(row.get(0)); member.setRealName(row.get(1)); if (Objects.equals(row.get(2), "f")) { member.setGender(Member.Gender.Female); } else { member.setGender(Member.Gender.Male); }
member.setDept(Integer.parseInt(row.get(3)));
member.setRole(Integer.parseInt(row.get(4)));
member.setPassword(row.get(5));
member.setEmail(row.get(6));
// TODO: 進(jìn)行測試
}
?
- Python 代碼
```python
is_header = True
for row in csv_data:
if is_header:
is_header = False
continue
# dict 類型的數(shù)據(jù)
member_data = {
"account": row[0],
"real_name": row[1],
"gender": row[2],
"dept": row[3],
"role": row[4],
"password": row[5],
"email": row[6]
}
# TODO: 進(jìn)行測試
?
2.4 封裝 Selenium WebDriver
封裝是一個面向?qū)ο缶幊痰母拍罘鲂溃敲嫦驅(qū)ο缶幊痰暮诵膶傩远煳。ㄟ^將代碼內(nèi)部實(shí)現(xiàn)進(jìn)行密封和包裝千扶,從而簡化編程。對Selenium進(jìn)行封裝的好處主要有如下三個方面:
- 使用成本低
- 不需要要求所有的測試工程師會熟練使用Selenium骆捧,而只需要會使用封裝以后的代碼
- 不需要對所有的測試工程師進(jìn)行完整培訓(xùn)澎羞。也避免工作交接的成本。
- 測試人員使用統(tǒng)一的代碼庫
- 維護(hù)成本低
- 通過封裝敛苇,在代碼發(fā)生大范圍變化和遷移的時候妆绞,不需要維護(hù)所有代碼,只需要變更封裝的部分即可
- 維護(hù)代碼不需要有大量的工程師枫攀,只需要有核心的工程師進(jìn)行封裝的維護(hù)即可
- 代碼安全性
- 對作為第三方的Selenium進(jìn)行封裝括饶,是代碼安全的基礎(chǔ)。
- 對于任何的代碼的安全隱患来涨,必須由封裝來解決巷帝,使得風(fēng)險可控。
- 使用者并不知道封裝內(nèi)部的代碼結(jié)構(gòu)扫夜。
封裝的具體示例:
-
找到一個指定輸入框(selector)楞泼,并且輸入指定的字符(text)
type(selector, text)
不用在業(yè)務(wù)邏輯中,使用多次的
findElement(By.id(...))
public void type(String selector, String text) { WebElement we = this.locateElement(selector); we.clear(); we.sendKeys(text); }
-
找到一個可以點(diǎn)擊的元素(selector)笤闯,并且點(diǎn)擊(click)
click(selector)
public void click(String selector) { this.locateElement(selector).click(); }
-
找到一個指定的frame堕阔,并且切換進(jìn)去
switchToFrame(selector)
public void switchToFrame(String selector) { WebElement we = this.locateElement(selector); this.baseDriver.switchTo().frame(we); }
-
找到一個指定的select,并且通過index進(jìn)行選擇
selectByIndex(selector, index)
public void selectByIndex(String selector, int index) { WebElement we = this.locateElement(selector); Select s = new Select(we); s.selectByIndex(index); }
以上的代碼是封裝了locateElement()
的幾種方法颗味,在具體使用封裝過的代碼的時候超陆,只需要簡單的調(diào)用即可。接下來的重點(diǎn)浦马,是介紹 locateElement(selector)
的封裝方式时呀。
- 查找元素:
findElement(By...)
- 支持各種的查找:8種方式都需要支持,必須通過
selector
顯示出分類-
selector
中需要包含一個特殊符號 - 實(shí)例化 封裝好的類的時候晶默,需要約定好是什么特殊符號
- 強(qiáng)制性用
硬編碼 hard code
來實(shí)例化谨娜,例如,
或者?
或者 其他非常用字符=>
- 或者,構(gòu)造方法中磺陡,傳遞
this.byChar
- 強(qiáng)制性用
-
- 要把查找到元素的返回給調(diào)用的地方:必須要有返回值趴梢,類型是
WebElement
private WebElement locateElement(String selector) {
WebElement we;
// 如果定位符中 有 分隔符,那么就從分隔符處分成兩段
// 第一段是By
// 第二段是真正的定位符
// 如果沒有分隔符币他,就默認(rèn)用 id 定位
if (!selector.contains(this.byChar)) {
// 用 id 定位
we = this.baseDriver.findElement(By.id(selector));
} else {
// 用 分隔符 分成兩個部分
String by = selector.split(this.byChar)[0];
String value = selector.split(this.byChar)[1];
we = findElementByChar(by, value);
}
return we;
}
- 接下來的重點(diǎn)坞靶,是實(shí)現(xiàn)
findElementByChar(by, value)
private WebElement findElementByChar(String by, String value) {
WebElement we = null;
switch (by.toLowerCase()) {
case "id":
case "i":
we = this.baseDriver.findElement(By.id(value));
break;
case "css_selector":
case "css":
case "cssselector":
case "s":
we = this.baseDriver.findElement(By.cssSelector(value));
break;
case "xpath":
case "x":
we = this.baseDriver.findElement(By.xpath(value));
break;
case "link_text":
case "link":
case "text":
case "linktext":
case "l":
we = this.baseDriver.findElement(By.linkText(value));
break;
//TODO: other by type
}
return we;
}
使用上面的封裝類,就需要指定特定的 selector
類型 | 示例(分隔符以逗號, 為例) |
描述 |
---|---|---|
id | "account" 或者 "i,account" 或者 "id,account" | 分隔符左右兩側(cè)不可以空格 |
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 |
調(diào)用的具體示例
void logIn(String account, String password) throws InterruptedException {
BoxDriver driver = this.baseDriver;
driver.type("account", account);
driver.type("password", password);
driver.click("submit");
// 點(diǎn)擊登錄按鈕后蝴悉,需要等待瀏覽器刷新
Thread.sleep(2000);
}
至此彰阴,自動化測試的方案如下圖所示:
- 封裝 Selenium 為 BoxDriver
- 在 測試用例中,實(shí)例化 BoxDriver拍冠,產(chǎn)生 bd 對象
- 使用 bd 對象尿这,構(gòu)造 業(yè)務(wù)模塊的實(shí)例化對象廉丽,產(chǎn)生 common
- 使用 common 在測試用例中,構(gòu)建測試步驟
- 使用數(shù)據(jù)驅(qū)動的外部數(shù)據(jù)妻味,通過讀取正压,進(jìn)行測試
- 執(zhí)行整個用例
2.5 使用 Git 進(jìn)行源代碼管理
Git 是目前主流的源代碼管理工具,本文推薦的兩個 IDE 工具: JetBrains IDEA 和 JetBrains Pycharm 都是默認(rèn)支持 Git 的责球。只需要按照以下步驟進(jìn)行配置焦履,便可以通過 IDE 工具對代碼進(jìn)行提交,這樣可以防止代碼丟失雏逾,以及方便的查詢代碼的修改歷史嘉裤,同時很方便團(tuán)隊(duì)的編碼。
使用編程工具提交代碼(用IDEA 或者 PyCharm)
- 在本地的Git項(xiàng)目文件夾中創(chuàng)建項(xiàng)目
- 比如:git\selenium_pro
- 用IDEA 創(chuàng)建
Maven
項(xiàng)目- 注意
project location
務(wù)必在git\selenium_pro
- 比如 項(xiàng)目名字
HelloSelenium
- 項(xiàng)目路徑:
git\selenium_pro\HelloSelenium
- 注意
- 編寫 代碼
- 選擇 IDEA 的 VCS |
Enable Version Control Integration
- 彈出的窗口選擇 Git
- 所有的代碼文件名字變成紅色
- 右鍵 左側(cè)的項(xiàng)目名字
HelloSelenium
- 選擇
Git
|Add
- 所有的代碼文件名字變成綠色
- 選擇
- 右鍵 左側(cè)的項(xiàng)目名字
HelloSelenium
- 選擇
Git
|Commit Directory
- 左側(cè)填寫 說明
- 右側(cè)勾選
Reformat Code
- 選擇右下角
Commit And Push
- "XX文件已經(jīng)committed"以后栖博,點(diǎn)擊
Push
- 輸入用戶名 + 密碼屑宠,勾選 remember password
- push successful
- 選擇
3. 架構(gòu)的構(gòu)建
將第二部分的自動化測試方案付諸實(shí)踐,再對自動化測試的結(jié)果生成測試報告仇让,便基本上實(shí)現(xiàn)了自動化架構(gòu)的構(gòu)建典奉。比較重要的地方是第三方工具 Selenium WebDriver 的封裝工作。事實(shí)上丧叽,如果封裝了別的工具卫玖,便可以實(shí)現(xiàn)其他方面的自動化測試。
3.1 代碼的構(gòu)建
在第二部分的基礎(chǔ)上踊淳,我們添加上去測試組織和測試報告的功能假瞬,并且將不同作用的代碼分開放入不同的文件夾中,實(shí)現(xiàn)代碼的構(gòu)建迂尝。
-
測試用例的組織和執(zhí)行
- 測試集合 test suite
- 測試用例的集合
- 多個測試用例的類
- 測試用例類的方法
- 測試用例的集合
- 測試運(yùn)行 運(yùn)行 test suite
具體的方法
-
TestNG
在項(xiàng)目中脱茉,創(chuàng)建
testng.xml
文件如下<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="Default Suite"> <test name="SolutionSelenium"> <classes> <class name="cases.LoginTests"> <methods> <!-- 添加指定的測試 --> <include name="testLoginByCsv"/> <include name="testLoginByCsv2"/> <include name="testLoginByCsv3"/> </methods> </class> <!-- cases.LoginTests --> <class name="cases.AdminTests"> <methods> <!-- 去除指定的測試 --> <exclude name="testAddMember"/> </methods> </class> </classes> </test> <!-- SolutionSelenium --> </suite> <!-- Default Suite -->
- 指定測試的類
- 指定測試的類中的方法
- include: 一個個方法包含進(jìn)來
- exclude: 去除指定的方法
然后,編寫測試入口的腳本:Main.java
public class Main { public static void main(String[] args) { TestNG test = new TestNG(); List<String> suites = new ArrayList<>(); suites.add("testng.xml"); test.setTestSuites(suites); test.run(); } }
?
- 測試集合 test suite
-
測試報告的生成
-
TestNG
測試報告
TestNG 自帶的測試報告 xml / html
-
ReportNG 測試報告的插件垄开,停止開發(fā)和支持琴许。
-
ExtentReport 測試報告
- TestNG有默認(rèn)的測試報告生成器 Listener
- 使用 ExtentReport 重寫一個 Listener
- 讓 TestNG 使用我們寫好的 Listener 生成報告
- TestNG有默認(rèn)的測試報告生成器 Listener
-
步驟
-
引入 ExtentReport 到 pom.xml
<dependency> <groupId>com.relevantcodes</groupId> <artifactId>extentreports</artifactId> <version>2.41.2</version> </dependency>
- 目前 ExtentReport 有兩個版本:2 和 3
- 2 全部開源的
- 3 有付費(fèi)版和開源版
- 官方網(wǎng)址:http://extentreports.com/community/
- 目前 ExtentReport 有兩個版本:2 和 3
-
編寫 Listener : ExtentReporterNgListener
@Override public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) { Date date = new Date(); SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_HHmmss"); String time = formatter.format(date); String reportName = String.format("ExtentReportTestNG_%s.html", time); // 創(chuàng)建報告 // NetworkMode.OFFLINE 支持?jǐn)嗑W(wǎng)查看報告 this.extent = new ExtentReports( outputDirectory + File.separator + reportName, true, NetworkMode.OFFLINE); for (ISuite suite : suites) { Map<String, ISuiteResult> result = suite.getResults(); for (ISuiteResult r : result.values()) { ITestContext context = r.getTestContext(); // 創(chuàng)建測試節(jié)點(diǎn) buildTestNodes(context.getPassedTests(), LogStatus.PASS); buildTestNodes(context.getFailedTests(), LogStatus.FAIL); buildTestNodes(context.getSkippedTests(), LogStatus.SKIP); } } extent.flush(); extent.close(); }
-
修改 testng.xml
在
<suite />
中添加<listener/>
<listeners> <listener class-name="runner.ExtentReporterNgListener"/> </listeners>
正常運(yùn)行測試
-
-
-
unittest 的操作
-
添加 test suite
suite = unittest.TestSuite() suite.addTest(LoginTests("test_login_by_csv")) suite.addTest(LoginTests("test_login_by_csv2")) suite.addTest(AdminTests("test_add_member_by_csv"))
-
這里可以配置測試,到外部文件说榆,數(shù)據(jù)庫中等虚吟。
測試類,測試方法 LoginTests,test_login_by_csv LoginTests,test_login_by_csv2 AdminTests,test_add_member_by_csv
?
-
-
實(shí)例化 test runner
# 測試報告的文件 test_time = time.strftime("%Y%m%d_%H%M%S", time.localtime()) report_file = open("reports\\ranzhi_automate_report_%s.html" % test_time, mode="wb") runner = HtmlTestRunner(stream=report_file, verbosity=2, title="然之系統(tǒng)自動化測試報告", description="具體測試報告內(nèi)容如下: ")
HtmlTestRunner : 第三方測試報告運(yùn)行器
-
用 test runner 去執(zhí)行測試,產(chǎn)生報告签财。
runner.run(suite)
?
-
3.2 使用持續(xù)集成
持續(xù)集成,Continuous integration 偏塞,簡稱CI唱蒸。隨著軟件開發(fā)復(fù)雜度的不斷提高,團(tuán)隊(duì)開發(fā)成員間如何更好地協(xié)同工作以確保軟件開發(fā)的質(zhì)量已經(jīng)慢慢成為開發(fā)過程中不可回避的問題灸叼。尤其是近些年來神汹,敏捷(Agile) 在軟件工程領(lǐng)域越來越紅火庆捺,如何能再不斷變化的需求中快速適應(yīng)和保證軟件的質(zhì)量也顯得尤其的重要。
持續(xù)集成正是針對這一類問題的一種軟件開發(fā)實(shí)踐屁魏。首先我們看一下滔以,敏捷教父 Martin Fowler
對持續(xù)集成的定義:
Martin Fowler:Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily - leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible. Many teams find that this approach leads to significantly reduced integration problems and allows a team to develop cohesive software more rapidly.
具體定義:持續(xù)集成式一種軟件開發(fā)實(shí)踐。它倡導(dǎo)團(tuán)隊(duì)的成員必須經(jīng)常的集成他們的工作氓拼,通常至少每天一次甚至更多次集成你画。每次集成都需要通過自動化的構(gòu)建(包括編譯代碼、構(gòu)建應(yīng)用桃漾、部署程序以及自動化測試)來驗(yàn)證坏匪,從而盡早盡快的發(fā)現(xiàn)集成中的錯誤。大量的團(tuán)隊(duì)利用這樣的方式來更快的開發(fā)內(nèi)聚的軟件撬统。大大減少此過程中的集成問題适滓。
持續(xù)集成強(qiáng)調(diào)開發(fā)人員提交了新代碼之后,立刻進(jìn)行構(gòu)建恋追、(單元凭迹、自動化)測試。根據(jù)測試結(jié)果苦囱,我們可以確定新代碼和原有代碼能否正確地集成在一起蕊苗。
首先,解釋下集成沿彭。我們所有項(xiàng)目的代碼都是托管在SVN服務(wù)器上朽砰。每個項(xiàng)目都要有若干個單元測試,并有一個所謂集成測試喉刘。所謂集成測試就是把所有的單元測試跑一遍以及其它一些能自動完成的測試瞧柔。只有在本地電腦上通過了集成測試的代碼才能上傳到SVN服務(wù)器上,保證上傳的代碼沒有問題睦裳。所以造锅,集成指集成測試。
再說持續(xù)廉邑。不言而喻哥蔚,就是指長期的對項(xiàng)目代碼進(jìn)行集成測試。既然是長期蛛蒙,那肯定是自動執(zhí)行的糙箍,否則,人工執(zhí)行則沒有保證牵祟,而且耗人力深夯。對此,我們有一臺服務(wù)器,它會定期的從SVN中檢出代碼咕晋,并編譯雹拄,然后跑集成測試。每次集成測試結(jié)果都會記錄在案掌呜。完成這方面工作的就是下面要介紹的Jenkins軟件滓玖。當(dāng)然,它的功能遠(yuǎn)不止這些质蕉。在我們的項(xiàng)目中势篡,執(zhí)行這個工作的周期是1天。也就是饰剥,服務(wù)器每1天都會準(zhǔn)時地對SVN服務(wù)器上的最新代碼自動進(jìn)行一次集成測試殊霞。
通過持續(xù)基礎(chǔ),可以將自動化測試良好的應(yīng)用起來汰蓉,只要是代碼發(fā)生了變動绷蹲,或者是進(jìn)行代碼的構(gòu)建,那么在構(gòu)建后都可以通過持續(xù)基礎(chǔ)顾孽,自動的執(zhí)行測試腳本祝钢,驗(yàn)證改進(jìn)的功能。
當(dāng)前主要的持續(xù)基礎(chǔ)工具有:
- Jenkins
- TeamCity
通過持續(xù)集成若厚,可以進(jìn)一步完善自動化測試的架構(gòu)拦英,使得自動化測試真正的幫助項(xiàng)目,保證測試質(zhì)量测秸。
3.3 參考代碼
本文的示例代碼疤估,放到了Github上面,以下附上Github的地址霎冯。為了簡化操作铃拇,本代碼使用了 Selenium 2 的環(huán)境,搭配 46.0(含)以下的火狐瀏覽器沈撞,以便各位參考慷荔。
代碼將會持續(xù)更新。
代碼地址:https://github.com/lintyleo/seleniumpro
再次感謝各位花時間閱讀本文缠俺,由于時間緊迫显晶,以及才疏學(xué)淺,文中難免出現(xiàn)各種疏漏壹士,還請各位多多指教磷雇。不當(dāng)之處,還歡迎各位指出墓卦,在此謝過倦春。