測試是一種思維,包括情感思維和智力思維,情感思維主要體現(xiàn)在一句俗語:思想決定行動上(要懷疑一切),智力思維主要體現(xiàn)在測試用例的設(shè)計上。具有了這樣的思想,就會找出更多的bug。(^_^個人認(rèn)為,不代表官方立場)
對于一個web網(wǎng)站來說,主要從這么幾個大的方面來進(jìn)行測試:
1、 功能測試;2、 界面測試;3、 易用性測試;4、兼容性測試;5、 鏈接測試;6、 業(yè)務(wù)流程測試;7、 安全性測試
下面主要從以上七個方面進(jìn)行敘述:
一、功能測試
測試用例是測試的核心,測試用例的設(shè)計是一種思維方式的體現(xiàn),在用例的設(shè)計中,用的比較多的方法是邊界值分析法和等價類劃分法,下面主要從輸入框,搜索功能,添加、修改功能,刪除功能,注冊、登錄功能以及上傳圖片功能等11個方面進(jìn)行總結(jié)說明。
1、輸入框
輸入框是測試中最容易出現(xiàn)bug的地方,所以在測試時,一定要多加注意。




2、搜索功能
?。?)比較長的名稱是否能查到?
(2)空格 或空
(3)名稱中含有特殊字符,如:' $ % & *以及空格等
?。?)關(guān)鍵詞前面或后面有空格
?。?)如果支持模糊查詢,搜索名稱中任意一個字符是否能搜索到
(6)輸入系統(tǒng)中不存在與之匹配的條件
?。?)兩個查詢條件是否為2選1,來回選擇是否出現(xiàn)頁面錯誤
?。?)輸入腳本語言,如:<script>alter(“abc”)</script>等
3、添加、修改功能
?。?)是否支持tab鍵
3、添加、修改功能
?。?)是否支持tab鍵
(2)是否支持enter鍵
?。?)不符合要求的地方是否有錯誤提示
?。?)保存后,是否也插入到數(shù)據(jù)庫中?
?。?)字段唯一的,是否可以重復(fù)添加
?。?)對編輯頁列表中的每個編輯項進(jìn)行修改,點擊保存,是否保存成功?
?。?)對于必填項,修改為空、空格或其他特殊符號,是否可以編輯成功
(8)在輸入框中,直接回車
?。?)是否能夠連續(xù)添加
?。?0)在編輯的時候,要注意編輯項的長度限制,有時,添加時有長度限制,但編輯時卻沒有(添加和修改規(guī)則是否一致)
(11)添加時,字段是唯一的,不允許重復(fù),但有時,編輯時,卻可以修改為相同字段(相同字段包括是否區(qū)分大小寫以及在輸入內(nèi)容的前后輸入空格)
(12)添加含有特殊符號或空格的內(nèi)容
?。?3)對于有圖片上傳功能的編輯框,對于沒有上傳的圖片,查看編輯頁面時,是否顯示默認(rèn)圖片,如果上傳了圖片,是否顯示為上傳圖片?
4、刪除功能
?。?)輸入正確數(shù)據(jù)前加空格,看是否能正確刪除?
?。?)是否支持enter鍵
?。?)是否能連續(xù)刪除多個產(chǎn)品?當(dāng)只有一條數(shù)據(jù)時,能否成功刪除?
?。?)刪除一條數(shù)據(jù)后,能否再添加相同的數(shù)據(jù)?
(5)當(dāng)提供能一次刪除多條信息的功能時,注意,刪除的數(shù)據(jù)是否正確?
?。?)不選擇任何信息,直接點擊刪除按鈕,看有什么錯誤提示?
(7)刪除某條信息時,應(yīng)該有錯誤提示信息
前言
考察目前關(guān)于單元測試和JUnit的文章,要么是介紹單元測試的理論,要么是通過一個簡單的HelloWorld例子介紹工具的使用。這樣很容易使讀者在實際應(yīng)用中無從下手。因為只有工具而沒有理論的指導(dǎo),將嚴(yán)重消弱了工具的作用,最終只能是沙灘建樓,達(dá)不到預(yù)期的目標(biāo);只有理論而沒有工具的支持,也使得理論難有很好的著力點,最終使理論流于空泛。本文試圖通過先講解單元測試?yán)碚摚M(jìn)而將這些理論結(jié)合到JUnit的使用當(dāng)中,最后通過對一個實用的、可以重用的時間操作類采用JUnit進(jìn)行單元測試來完整闡述單元測試的思想、方法、以及工具的使用。作者相信,只有通過這樣,才能讓讀者真正把單元測試做好。
1.簡介
1.1. 為什么要進(jìn)行單元測試
一個特定的開發(fā)組織或軟件應(yīng)用系統(tǒng)的測試水平取決于對那些未發(fā)現(xiàn)的Bug的潛在后果的重視程度。這種后果一方面常常會被軟件的開發(fā)人員所忽視,而另一方面卻有可能損害組織的信譽(yù),并且會導(dǎo)致對未來的市場產(chǎn)生負(fù)面的影響。相反地,一個可靠的軟件系統(tǒng)的良好的聲譽(yù)將有助于一個開發(fā)組織獲取未來的市場。
很多研究成果表明,無論什么時候作出修改都要進(jìn)行完整的回歸測試,在生命周期中盡早地對軟件產(chǎn)品進(jìn)行測試將使效率和質(zhì)量得到最好的保證。Bug發(fā)現(xiàn)得越晚,修改它所需的費用就越高,因此從經(jīng)濟(jì)角度來看,應(yīng)該盡可能早的查找和修改Bug。在修改費用變得過高之前,單元測試是一個在早期抓住Bug的機(jī)會。
相比后階段的測試,單元測試的創(chuàng)建更簡單、維護(hù)更容易,并且可以更方便的進(jìn)行重復(fù)。 從全程的費用來考慮,相比起那些復(fù)雜且曠日持久的集成測試,或是不穩(wěn)定的軟件系統(tǒng)來說, 單元測試所需的費用是很低的。研究顯示高達(dá)50%的維護(hù)工作量被花在那些總是會有的Bug的修改上面。如果這些Bug在開發(fā)階段被排除掉的話,那么工作量就可以節(jié)省下來。當(dāng)考慮到軟件維護(hù)費用可能會比最初的開發(fā)費用高出數(shù)倍的時候,這種潛在的對50%軟件維護(hù)費用的節(jié)省將對整個軟件生命周期費用產(chǎn)生重大的影響。
1.2. 什么是單元測試
單元測試是對最小的可測試軟件元素(單元)實施的測試,它所測試的內(nèi)容包括內(nèi)部結(jié)構(gòu)(如邏輯和數(shù)據(jù)流)以及單元的功能和可觀測的行為。這里的單元不一定是指一個具體的函數(shù)或一個類的方法,“單元”是:
?。?)可測試的、最小的、不可再分的程序模塊。
?。?)有明確的功能、規(guī)格定義。
(3)有明確的接口定義,清晰地與同一程序的其他單元劃分開來。
在具體實現(xiàn)時,單元測試也可能對應(yīng)的是多個程序文件中的一組函數(shù)。在一種傳統(tǒng)的結(jié)構(gòu)化編程語言中,比如C,要進(jìn)行測試的單元一般是函數(shù)或子過程。在象C++這樣的面向?qū)ο蟮恼Z言中,要進(jìn)行測試的基本單元是類。單元測試的原則同樣被擴(kuò)展到第四代語言(4GL)的開發(fā)中,在這里基本單元被典型地劃分為一個菜單或顯示界面。
1.3. 單元測試的一般方法
單元測試的方法一般分為兩類:白盒方法和黑盒方法。白盒方法通常是分析單元內(nèi)部結(jié)構(gòu)后通過對單元輸入輸出的用例構(gòu)造,達(dá)到單元內(nèi)程序路徑的最大覆蓋,盡量保證單元內(nèi)部程序運行路徑處理正確,它側(cè)重于單元內(nèi)部結(jié)構(gòu)的測試,依賴于對單元實施情況的了解。
黑盒方法通過對單元輸入輸出的用例構(gòu)造驗證單元的特性和行為,側(cè)重于核實單元的可觀測行為和功能,并不依賴于對單元實施情況的了解。進(jìn)行單元測試必須綜合使用上述兩個方法,否則,單元測試很可能就是不成功、不完整和不徹底的。
1.4. 單元測試的目標(biāo)
單元測試要達(dá)到的目標(biāo),總體來說就是保證單元內(nèi)部的處理是正確的、沒有遺漏和多余功能。細(xì)分而言,單元測試要達(dá)到以下幾個目標(biāo):
?。?)信息能否正確地流入和流出單元。
?。?)在單元工作過程中,其內(nèi)部數(shù)據(jù)能否保持其完整性,包括內(nèi)部數(shù)據(jù)的形式、內(nèi)容及相互關(guān)系不發(fā)生錯誤,也包括全局變量在單元中的處理和影響。
(3)在為限制數(shù)據(jù)加工而設(shè)置的邊界處,能否正確工作。
?。?)單元的運行能否做到滿足特定的邏輯覆蓋。
(5)單元中發(fā)生了錯誤,其中的出錯處理措施是否有效。
1.5. 為什么要使用JUnit進(jìn)行單元測試
1.5.1. 什么是JUnit
JUnit就是對程序代碼進(jìn)行單元測試的一種Java框架。通過每次修改程序之后測試代碼,程序員就可以保證代碼的的少量變動不會破壞整個系統(tǒng)。官方對JUnit的定義是“JUnit is a simple framework to write repeatable tests.”。
1.5.2. 自己編寫測試框架的弊病
自己編寫測試框架進(jìn)行單元測試一般有兩個方法。第一種方法是在要測試的類的main()方法中編寫測試代碼。隨著程序越變越大,這種開發(fā)方法很快就開始顯現(xiàn)出了缺陷:
?。?)混亂。類接口越大,main() 就越大。類可能僅僅因為正常的測試就變得非常龐大。
?。?)代碼膨脹。由于加入了測試,所以產(chǎn)品代碼比所需要的要大。
(3)測試不可靠。main() 是代碼的一部分,main() 就對其他開發(fā)者通過類接口無法訪問的私有成員和方法享有訪問權(quán)。出于這個原因,這種測試方法很容易出錯。
?。?)很難自動測試。要進(jìn)行自動測試,必須創(chuàng)建另一程序來將參數(shù)傳遞給 main()。第二種方法是編寫一個測試類框架,它雖然能夠克服上個方法的缺陷,但增加了開發(fā)組織維護(hù)這個測試類框架的工作量,為立即大規(guī)模的重用設(shè)置障礙。而且,由于這個測試框架是內(nèi)部開發(fā)的,存在著與業(yè)界難于交流和溝通的弊病。
1.5.3. JUnit的優(yōu)勢
?。?)需要編寫自己的框架。
?。?)它是開放源代碼,因此不需要購買框架。
(3)開放源代碼社區(qū)中的其他開發(fā)者會使用它,因此可以找到許多示例。
?。?)可以將測試代碼與產(chǎn)品代碼分開。
?。?)易于集成到構(gòu)建過程中。
2. 單元測試設(shè)計
2.1. 單元測試的一般過程
單元測試過程分為計劃、設(shè)計、實現(xiàn)、執(zhí)行、評估等幾個步驟,各步驟的任務(wù)如下:
2.1.1. 計劃
單元測試計劃需明確如下目標(biāo):
?。?)明確單元測試的測試對象,確定測試需求及測試通過的標(biāo)準(zhǔn),明確活動的輸出。
?。?)明確測試方法和需要運行的工具需求。
?。?)對工作量進(jìn)行估計,確定測試所用資源(包括人力資源和設(shè)備資源),創(chuàng)建測試任務(wù)的時間表,必要時需將一個單元測試任務(wù)分解成更細(xì)化的子任務(wù)進(jìn)行明確。
(4)對測試風(fēng)險進(jìn)行分析,制定相應(yīng)的應(yīng)急措施。
?。?)明確測試優(yōu)先級,制定測試取舍策略。
?。?)輸出單元測試計劃文檔。
2.1.2. 設(shè)計
單元測試的設(shè)計主要是完成方案和模型的確認(rèn),包括如下幾方面內(nèi)容:
(1)測試需求的進(jìn)一步細(xì)化,必要時需追溯到詳細(xì)設(shè)計文檔中的單元設(shè)計目標(biāo)。
(2)設(shè)計單元測試模型,包括與模型相關(guān)的工具的選用。
?。?)制定測試方案,包括模型的設(shè)計和實現(xiàn)、定義測試規(guī)程和用例的實現(xiàn)和組織。
?。?)輸出單元測試方案文檔。
2.1.3. 實現(xiàn)
單元測試實現(xiàn)主要是針對用例的實現(xiàn),包括如下幾個方面:
(1)參考測試模型和測試方案,制定具體的測試用例,創(chuàng)建可重用的測試腳本。
?。?)輸出單元用例文檔。
2.1.4. 執(zhí)行
根據(jù)單元測試的方案、用例對單元進(jìn)行測試,驗證測試的結(jié)果并記錄測試過程中出現(xiàn)的缺陷,主要保留執(zhí)行過程數(shù)據(jù)以備問題定位的回歸對比。
2.1.5. 評估
對單元測試的結(jié)果進(jìn)行評估,主要有如下幾個方面:
(1)實際測試過程的記錄,描述與計劃的差異和原因,包括補(bǔ)充或裁剪的測試項目清單。
(2)對測試過程完備性以及被測單元質(zhì)量的評價,包括用例執(zhí)行情況清單和匯總分析。
(3)主要從需求覆蓋和代碼覆蓋的角度進(jìn)行測試完備性的評估。
?。?)遺留問題記錄和可能的分析。
?。?)輸出單元測試報告。
2.2. 單元測試用例設(shè)計方法
測試用例的設(shè)計在單元測試中占有非常重要的地位,測試用例設(shè)計的好壞直接影響到測試的效果。確定測試用例之所以很重要,原因有以下幾方面:
(1)測試用例構(gòu)成了設(shè)計和制定測試過程的基礎(chǔ)。
(2)測試的“深度”與測試用例的數(shù)量成比例。由于每個測試用例反映不同的場景、條件或經(jīng)由產(chǎn)品的事件流,因而,隨著測試用例數(shù)量的增加,對產(chǎn)品質(zhì)量和測試流程也就越有信心。判斷測試是否完全的一個主要評測方法是基于需求的覆蓋,而這又是以確定、實施和/或執(zhí)行的測試用例的數(shù)量為依據(jù)的。
(3)測試工作量與測試用例的數(shù)量成比例。根據(jù)全面且細(xì)化的測試用例,可以更準(zhǔn)確地估計測試周期各連續(xù)階段的時間安排。
?。?)測試設(shè)計和開發(fā)的類型以及所需的資源主要都受控于測試用例。測試用例通常根據(jù)它們所關(guān)聯(lián)關(guān)系的測試類型或測試需求來分類,而且將隨類型和需求進(jìn)行相應(yīng)地改變。
最佳方案是為每個測試需求至少編制兩個測試用例:
(1)一個測試用例用于證明該需求已經(jīng)滿足,通常稱作正面測試用例。
(2)另一個測試用例反映某個無法接受、反常或意外的條件或數(shù)據(jù),用于論證只有在所需條件下才能夠滿足該需求,這個測試用例稱作負(fù)面測試用例。
單元測試既可以是白盒測試也可以是黑盒測試。白盒測試主要是檢查程序的內(nèi)部結(jié)構(gòu)、邏輯、循環(huán)和路徑。其常用測試用例設(shè)計方法有:邏輯覆蓋和基本路徑測試。根據(jù)覆蓋測試的目標(biāo)不同,邏輯覆蓋又可分為:語句覆蓋,判定覆蓋,判定-條件覆蓋,條件組合覆蓋及路徑覆蓋等。白盒測試用例設(shè)計還可用到:狀態(tài)轉(zhuǎn)移測試、數(shù)據(jù)定義-使用測試、等價類劃分、邊界值分析等。黑盒測試注重對程序功能方面的要求,它只用到程序的規(guī)格說明,沒有用到程序的內(nèi)部結(jié)構(gòu)。其常用測試用例方法有:規(guī)范(規(guī)格)導(dǎo)出、等價類劃分、邊界值分析法、錯誤推測法和因果圖分析方法。下面將簡要介紹各個方法,更詳細(xì)的說明請讀者自行參考相關(guān)的測試?yán)碚摃?/p>
2.2.1. 語句覆蓋
語句覆蓋就是設(shè)計若干個測試用例,運行所測程序,使得每一可執(zhí)行語句至少執(zhí)行一次。
2.2.2. 判定覆蓋
判定覆蓋就是設(shè)計若干個測試用例,運行所測程序,使得程序中每個判斷的取TURE分支和取FALSE分支至少經(jīng)歷一次。
2.2.3. 條件覆蓋
條件覆蓋就是設(shè)計若干個測試用例,運行所測程序,使得程序中每個判斷的每個條件的可能取值至少執(zhí)行一次。
2.2.4. 判定-條件覆蓋
判定-條件覆蓋就是設(shè)計足夠的測試用例,使得判斷中每個條件的所有可能取值至少執(zhí)行一次,同時每個判斷的所有可能判斷結(jié)果至少執(zhí)行一次。也就是說要求各個判斷的所有可能的條件取值組合至少執(zhí)行一次。
2.2.5. 條件組合覆蓋
條件組合覆蓋就是設(shè)計足夠的測試用例,運行所測程序,使得每個判斷得所有可能得條件取值組合至少執(zhí)行一次。
2.2.6. 路徑覆蓋
路徑測試就是設(shè)計足夠的測試用例,覆蓋程序中所有可能的路徑。
2.2.7. 規(guī)范(規(guī)格)導(dǎo)出法
規(guī)范導(dǎo)出法是根據(jù)相關(guān)的規(guī)范描述來設(shè)計測試用例。每一個測試用例用來測試一個或多個規(guī)范陳述語句。一個比較實際的方法是根據(jù)陳述規(guī)范所用語句的順序來相應(yīng)地為被測單元設(shè)計測試用例。
2.2.8. 狀態(tài)轉(zhuǎn)移測試法
對于那些以狀態(tài)機(jī)作為模型或設(shè)計為狀態(tài)機(jī)的軟件,狀態(tài)轉(zhuǎn)移測試是合適的測試方法。測試用例通過能導(dǎo)致狀態(tài)遷移的事件來測試狀態(tài)之間的轉(zhuǎn)換。
2.2.9. 數(shù)據(jù)定義-使用測試法
數(shù)據(jù)定義是指數(shù)據(jù)項被賦值的地方,數(shù)據(jù)使用是指數(shù)據(jù)項被讀或使用的地方。目的是設(shè)計測試用例以驅(qū)動執(zhí)行通過數(shù)據(jù)定義于使用之間的路徑。
3. 使用JUnit進(jìn)行單元測試的一般步驟
3.1. 獲得Junit
下載得到JUnit的安裝軟件包。
3.2. 安裝JUnit安裝JUnit只需要很簡單的兩個步驟,下面是安裝Junit的步驟:
?。?)解開DownLoad下來的junit.zip文件。
?。?)增加junit.jar到classpath中。例如,set classpath = %classpath%; INSTALL_DIR\Junit3.7\junit.jar經(jīng)過這兩步,就可以開始使用JUnit了。
3.3. 使用JUnit編寫測試代碼的一般步驟
使用JUnit編寫測試代碼的一般步驟是:
?。?)定義測試類名稱,一般是將要測試的類名后附加Test。
?。?)引入JUnit框架包。import junit.framework.*。
(3)測試類繼承JUnit的TestCase類。
?。?)實現(xiàn)類的構(gòu)造方法,可以在構(gòu)造方法中簡單的調(diào)用super(name)即可。
?。?)實現(xiàn)類的main()方法,在main()方法中簡單調(diào)用junit.textui.TestRunner.run(DateUtilTest.class)來指定執(zhí)行測試類。
(6)重載setUp()和tearDown()方法,setUp()方法用于執(zhí)行每個測試用例時進(jìn)行環(huán)境的初始化工作(比如打開數(shù)據(jù)庫連接),tearDown()方法用于執(zhí)行每個測試用例后清除環(huán)境(比如關(guān)閉數(shù)據(jù)庫連接)。
?。?)編寫每個測試用例,一般是要測試的方法前附加test。
完整的代碼框架如下所示:
import junit.framework.*; public class DateUtilTest extends TestCase { /** * 構(gòu)造函數(shù) */ public DateUtilTest(String name) { super(name); } /** * 主方法 */ public static void main(String args[]) { junit.textui.TestRunner.run(DateUtilTest.class); } /** * 測試前的初始化 */ protected void setUp() { } /** * 清除測試環(huán)境 */ protected void tearDown(){ } /** * 測試用例1 */ public void testGetDateFormat() { } } |
4. 使用JUnit進(jìn)行單元測試Java應(yīng)用一例
4.1. 定義接口
按照J(rèn)Unit的思想,“先有測試代碼,后有實現(xiàn)代碼”,在編寫代碼之前,首先應(yīng)該確定接口。本樣例的接口定義如下:
/** * <p>Title: 時間和日期的工具類</p> * <p>Description: DateUtil類包含了標(biāo)準(zhǔn)的時間和日期格式,以及這些格式在字符串及日期之間轉(zhuǎn) 換的方法</p> * <p>Copyright: Copyright (c) 2002</p> * <p>Company: </p> * @author kzx * @version 1.0 */ import java.text.*; import java.util.*; public abstract class DateUtil { /** * 標(biāo)準(zhǔn)日期格式 */ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MM/dd/yyyy"); /** * 標(biāo)準(zhǔn)時間格式 */ private static final SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("MM/dd/yyyy HH:mm"); /** * 帶時分秒的標(biāo)準(zhǔn)時間格式 */ private static final SimpleDateFormat DATE_TIME_EXTENDED_FORMAT = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); /** * ORA標(biāo)準(zhǔn)日期格式 */ private static final SimpleDateFormat ORA_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); /** * ORA標(biāo)準(zhǔn)時間格式 */ private static final SimpleDateFormat ORA_DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); /** * 帶時分秒的ORA標(biāo)準(zhǔn)時間格式 */ private static final SimpleDateFormat ORA_DATE_TIME_EXTENDED_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss"); /** * 創(chuàng)建一個標(biāo)準(zhǔn)日期格式的克隆 * * @return 標(biāo)準(zhǔn)日期格式的克隆 */ public static synchronized DateFormat getDateFormat() { /** * 詳細(xì)設(shè)計: * 1.返回DATE_FORMAT */ return null; } /** * 創(chuàng)建一個標(biāo)準(zhǔn)時間格式的克隆 * * @return 標(biāo)準(zhǔn)時間格式的克隆 */ public static synchronized DateFormat getDateTimeFormat() { /** * 詳細(xì)設(shè)計: * 1.返回DATE_TIME_FORMAT */ return null; } |
/** * 創(chuàng)建一個標(biāo)準(zhǔn)ORA日期格式的克隆 * * @return 標(biāo)準(zhǔn)ORA日期格式的克隆 */ public static synchronized DateFormat getOraDateFormat() { /** * 詳細(xì)設(shè)計: * 1.返回ORA_DATE_FORMAT */ return null; } /** * 創(chuàng)建一個標(biāo)準(zhǔn)ORA時間格式的克隆 * * @return 標(biāo)準(zhǔn)ORA時間格式的克隆 */ public static synchronized DateFormat getOraDateTimeFormat() { /** * 詳細(xì)設(shè)計: * 1.返回ORA_DATE_TIME_FORMAT */ return null; } /** * 將一個日期對象轉(zhuǎn)換成為指定日期、時間格式的字符串。 * 如果日期對象為空,返回一個空字符串,而不是一個空對象。 * * @param theDate 要轉(zhuǎn)換的日期對象 * @param theDateFormat 返回的日期字符串的格式 * @return 轉(zhuǎn)換結(jié)果 */ public static synchronized String toString(Date theDate, DateFormat theDateFormat) { /** * 詳細(xì)設(shè)計: * 1.theDate為空,則返回"" * 2.否則使用theDateFormat格式化 */ return null; } /** * 將日期對象轉(zhuǎn)換成為指定日期、時間格式的字符串形式。如果日期對象為空,返回 * 一個空字符串對象,而不是一個空對象。 * * @param theDate 將要轉(zhuǎn)換為字符串的日期對象。 * @param hasTime 如果返回的字符串帶時間則為true * @return 轉(zhuǎn)換的結(jié)果 */ public static synchronized String toString(Date theDate, boolean hasTime) { /** * 詳細(xì)設(shè)計: * 1.如果有時間,則設(shè)置格式為getDateTimeFormat的返回值 * 2.否則設(shè)置格式為getDateFormat的返回值 * 3.調(diào)用toString(Date theDate, DateFormat theDateFormat) */ return null; } |
/** * 將日期對象轉(zhuǎn)換成為指定ORA日期、時間格式的字符串形式。如果日期對象為空,返回 * 一個空字符串對象,而不是一個空對象。 * * @param theDate 將要轉(zhuǎn)換為字符串的日期對象。 * @param hasTime 如果返回的字符串帶時間則為true * @return 轉(zhuǎn)換的結(jié)果 */ public static synchronized String toOraString(Date theDate, boolean hasTime) { /** * 詳細(xì)設(shè)計: * 1.如果有時間,則設(shè)置格式為getOraDateTimeFormat()的返回值 * 2.否則設(shè)置格式為getOraDateFormat()的返回值 * 3.調(diào)用toString(Date theDate, DateFormat theDateFormat) */ return null; } /** * 取得指定日期的所處月份的第一天 * * @param date 指定日期。 * @return 指定日期的所處月份的第一天 */ public static java.util.Date getFirstDayOfMonth(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.設(shè)置為1號 */ return null; } /** * 取得指定日期的所處月份的最后一天 * * @param date 指定日期。 * @return 指定日期的所處月份的最后一天 */ public static synchronized java.util.Date getLastDayOfMonth(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.如果date在1月,則為31日 * 2.如果date在2月,則為28日 * 3.如果date在3月,則為31日 * 4.如果date在4月,則為30日 * 5.如果date在5月,則為31日 * 6.如果date在6月,則為30日 * 7.如果date在7月,則為31日 * 8.如果date在8月,則為31日 * 9.如果date在9月,則為30日 * 10.如果date在10月,則為31日 * 11.如果date在11月,則為30日 * 12.如果date在12月,則為31日 * 1.如果date在閏年的2月,則為29日 */ return null; } /** * 取得指定日期的所處星期的第一天 * * @param date 指定日期。 * @return 指定日期的所處星期的第一天 */ public static synchronized java.util.Date getFirstDayOfWeek(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.如果date是星期日,則減0天 * 2.如果date是星期一,則減1天 * 3.如果date是星期二,則減2天 * 4.如果date是星期三,則減3天 * 5.如果date是星期四,則減4天 * 6.如果date是星期五,則減5天 * 7.如果date是星期六,則減6天 */ return null; } |
/** * 取得指定日期的所處星期的最后一天 * * @param date 指定日期。 * @return 指定日期的所處星期的最后一天 */ public static synchronized java.util.Date getLastDayOfWeek(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.如果date是星期日,則加6天 * 2.如果date是星期一,則加5天 * 3.如果date是星期二,則加4天 * 4.如果date是星期三,則加3天 * 5.如果date是星期四,則加2天 * 6.如果date是星期五,則加1天 * 7.如果date是星期六,則加0天 */ return null; } /** * 取得指定日期的下一天 * * @param date 指定日期。 * @return 指定日期的下一天 */ public static synchronized java.util.Date getNextDay(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.指定日期加1天 */ return null; } /** * 取得指定日期的下一個星期 * * @param date 指定日期。 * @return 指定日期的下一個星期 */ public static synchronized java.util.Date getNextWeek(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.指定日期加7天 */ return null; } /** * 取得指定日期的下一個月 * * @param date 指定日期。 * @return 指定日期的下一個月 */ public static synchronized java.util.Date getNextMonth(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.指定日期的月份加1 */ return null; } /** * 取得指定日期的下一個星期的第一天 * * @param date 指定日期。 * @return 指定日期的下一個星期的第一天 */ public static synchronized java.util.Date getFirstDayOfNextWeek(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.調(diào)用getNextWeek設(shè)置當(dāng)前時間 * 2.以1為基礎(chǔ),調(diào)用getFirstDayOfWeek */ return null; } /** * 取得指定日期的下一個月的第一天 * * @param date 指定日期。 * @return 指定日期的下一個月的第一天 */ public static synchronized java.util.Date getFirstDayOfNextMonth(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.調(diào)用getNextMonth設(shè)置當(dāng)前時間 * 2.以1為基礎(chǔ),調(diào)用getFirstDayOfMonth */ return null; } /** * 取得指定日期的下一個星期的最后一天 * * @param date 指定日期。 * @return 指定日期的下一個星期的最后一天 */ public static synchronized java.util.Date getLastDayOfNextWeek(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.調(diào)用getNextWeek設(shè)置當(dāng)前時間 * 2.以1為基礎(chǔ),調(diào)用getLastDayOfWeek */ return null; } /** * 取得指定日期的下一個月的最后一天 * * @param date 指定日期。 * @return 指定日期的下一個月的最后一天 */ public static synchronized java.util.Date getLastDayOfNextMonth(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.調(diào)用getNextMonth設(shè)置當(dāng)前時間 * 2.以1為基礎(chǔ),調(diào)用getLastDayOfMonth */ return null; } /** * 判斷指定日期的年份是否是閏年 * * @param date 指定日期。 * @return 是否閏年 */ public static synchronized boolean isLeapYear(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.被400整除是閏年,否則 * 2.不能被4整除則不是閏年 * 3.能被4整除同時不能被100整除則是閏年 * 3.能被4整除同時能被100整除則不是閏年 */ return false; } /** * 得到指定日期的后一個工作日 * * @param date 指定日期。 * @return 指定日期的后一個工作日 */ public static synchronized java.util.Date getNextWeekDay(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.如果date是星期五,則加3天 * 2.如果date是星期六,則加2天 * 3.否則加1天 */ return null; } /** * 得到指定日期的前一個工作日 * * @param date 指定日期。 * @return 指定日期的前一個工作日 */ public static synchronized java.util.Date getPreviousWeekDay(java.util.Date date){ /** * 詳細(xì)設(shè)計: * 1.如果date是星期日,則減3天 * 2.如果date是星期六,則減2天 * 3.否則減1天 */ return null; } } |
說起單元測試,多數(shù)同學(xué)應(yīng)該都知道或聽過,可能不少同學(xué)認(rèn)為自己也寫過,甚至覺得單元測試很簡單有什么好培訓(xùn)的?其實這個事情還真沒想象的那么簡單!我基本可以比較負(fù)責(zé)任的說,你若沒深入對單元測試做過研究,不知道Mock對象為何物的話,那么可能你以前寫過的單元測試壓根就不是單元測試。
單元測試是什么?
這個問題其實并不太容易一兩句話說得特別清楚。先借用下百度百科的定義:
單元測試是在軟件開發(fā)過程中要進(jìn)行的最低級別的測試活動,在單元測試活動中,軟件的獨立單元將在與程序的其他部分相隔離的情況下進(jìn)行測試。
從以上這句定義我們可以看到,兩個提取到到兩個非常關(guān)鍵的字:最小粒度、隔離
● 單元測試是測試的最小單位,必須可信任的,可重復(fù)執(zhí)行的。
● 如果測試的范圍輕易的就會擴(kuò)展到其他類或同類的其他方法,那就不再是最小單位,也就不是單元測試了!
例如:
類A中的方法CallMethod中調(diào)用了類B中的方法DoMethod,如果在編寫測試的時候不把B類中的DoMethod隔離出來,造成單元測試CallMethod方法時,實際實行了DoMethod方法,那么這個測試方法不能算作是單元測試。(如何隔離會在后文詳解)
單元測試的目的是什么?
有人曾給我一段非常簡單的代碼片段:一個方法,里面只是調(diào)用若干個其他方法,甚至也都沒有任何返回值,然后問我這種代碼寫單元測試有任何價值?!完全是浪費體力?。?/p>
public class ClassA { public void CallMethod() { DoSomethingForYou(); DoSomethingForThem(); DoSomethingForMe(); } } |
其實提出這個疑問主要有兩個原因?qū)е拢何茨芾斫鈫卧獪y試的目的是什么以及這段代碼的可測試性并不高。(可測試性以及如何提高將在后面章節(jié)介紹)
單元測試的目的是用來確保程式的邏輯如你預(yù)期的方式執(zhí)行,而并不是用來驗證是否符合客戶的需求的!通過單元測試來建立一道堅實的保障,防止代碼在日后的修改中不會被破壞掉。
是不是很失望?單元測試并不是用來驗證代碼是否符合需求的。
事實上,單元測試是白盒測試的一種,而且需要開發(fā)人員來完成,最好是誰開發(fā)的代碼就該誰來編寫單元測試代碼,因為代碼的編寫者最熟悉該段代碼的目的,進(jìn)而編寫出驗證該目的是否達(dá)到的單元測試代碼。
單元測試并不能用來代替其他測試手段,不過是實踐過程中確實會很有效的幫助開發(fā)人員自查代碼,進(jìn)而發(fā)現(xiàn)一些潛在的BUG。但這只是一個額外的收獲,若不是采用TDD那樣的測試先行開發(fā)方式,那么單元測試的根本目的不是用于也無法檢驗當(dāng)前代碼是否存在BUG的!
上面有說到單元測試最好是由開發(fā)人員自己來編寫,用于驗證該段代碼是否符合開發(fā)者開發(fā)的預(yù)期要求。這里可能會有個疑問,既然開發(fā)者自己已經(jīng)很清楚自己想要 結(jié)果是什么,直接運行一遍代碼實際跑一次,通過斷點調(diào)試不是就可以很方便的驗證了嘛?再通過編寫代碼的形式,甚至比開發(fā)這個功能本身更多的代碼,去驗證這 個方法是否符合編寫的目的,不是很傻很笨很累的辦法么?
也許通過一個大家經(jīng)常會碰到的實際場景能更好的說明:
一個項目開始,項目經(jīng)理把需求拆解為若干個模塊分發(fā)給不同的開發(fā)人員去完成。這樣每個人可能只熟悉自己的那部分代碼。當(dāng)項目某個階段開發(fā)完成并上線后,可能部分開發(fā)人員會離開項目進(jìn)入別的新項目,留下個別人員繼續(xù)維護(hù);或者項目下階段開發(fā)新進(jìn)一大批人員并不熟悉當(dāng)前項目;當(dāng)然最常見的是,在修改BUG階段是無法完全做到誰產(chǎn)生的BUG就安排誰去修改。
那么這時候就會出現(xiàn)一種常見的情況:因為對當(dāng)前代碼要滿足的各種目的不熟悉,在修改一個模塊或者BUG的時候把原有正確的功能也影響到了!更要命的是,誰也不知道這個BUG出現(xiàn)了,等待測試人員需要去重新發(fā)現(xiàn)一遍。
于是項目經(jīng)理會發(fā)現(xiàn),每次只要做了代碼修改,無論是重構(gòu)還是功能新增修改,還是修改了BUG,都無法知道當(dāng)前代碼的健壯性,以前編寫的東西是否依然正確可用?
然而如果這個項目在一開始就編寫了單元測試的話,我們可以通過方便的自動化單元測試框架運行所有的單元測試,進(jìn)而檢查在此次修改前的所有被單元測試所覆蓋的代碼是否依然正常運行(符合以前編寫的單元測試期望,如果驗證通過,則認(rèn)為原有代碼未受到影響)
由上我們可以看出,單元測試雖然增加了相當(dāng)大的開發(fā)工作量,但對于一個長期不斷改進(jìn)和維護(hù)的項目而言,單元測試反而是消減整體成本的一個有效手段,它能及時而準(zhǔn)確的發(fā)現(xiàn)在代碼修改之后,原來對代碼要求的功能是否都依然正確滿足。
但這里有個嚴(yán)重缺陷:單元測試無法檢測到某個方法修改后是否對其他方法造成了影響,只能檢測到被修改的方法本身的原有目的是否被影響(這個將在下面的與集成測試的區(qū)別中詳解)
也因此,個人覺得單元測試最適合的場景是基于TDD開發(fā)。若需求發(fā)生改變,修改了一個方法,而多數(shù)情況下也會去修改單元測試代碼,因為預(yù)期也發(fā)生了改變,這個時候又不能檢測到對其他代碼的影響,這時單元測試意義確實不大。
單元測試與集成測試的區(qū)別是什么?
多數(shù)人其實一直不能很好的區(qū)分集成測試和單元測試,甚至不少人一直理解的單元測試只能算是集成測試,但其實兩者的概念是完全不同的。
單元測試測試的對象是每一個獨立的方法,而且盡可能的隔離方法和其他方法以及其他外界依賴項;
集成測試的測試對象是被單元測試檢測后的方法與方法之間的調(diào)用關(guān)系,以及調(diào)用執(zhí)行過程是否符合預(yù)期。
● 針對項目的一部份或全部進(jìn)行測試,可以跨越不同的類別與方法,并可直接存取的外部資源。例如: File I/O, 數(shù)據(jù)庫操作, 網(wǎng)絡(luò)連接, …
● 通常做集成測試都會需要先設(shè)置(Configure)測試所需的環(huán)境,測試完畢后通常要清除測試所產(chǎn)生的殘留資料,以利下次測試或避免影響其他整合測試的結(jié)果。
○ ClassInitialize Attribute
○ ClassCleanup Attribute
○ TestInitialize Attribute
○ TestCleanup Attribute
以上這些屬性常用于集成測試,不能出現(xiàn)在單元測試中。
單元測試的三大基本要素(Trustworthiness/Maintainability/Readability)
1、信任你的測試代碼結(jié)果
1)你是否能信任你的測試結(jié)果?
2)當(dāng)它通過,我們有信心說被測試代碼一定工作。
3)當(dāng)它失敗,它一定證明被測試代碼是錯誤的。
4)如果你不斷的對測試結(jié)果失去信心,那么你也不會繼續(xù)堅持撰寫單元測試。有
5)許多人搞不清楚單元測試與集成測試的差別,以致于感覺自己寫的單元測試過于薄弱而不相信測試的結(jié)果。
6)如果你因為某些原因?qū)е聹y試失敗,直接去改Code或直接去改Test Code都不是好事,你的首要目的是要能找出測試失敗發(fā)生的主因,而非只是看錯誤這件事,這樣你才能信任你的測試程式。
2、測試代碼的可維護(hù)性
1)是否能夠持續(xù)的維護(hù)你的測試程式?
2)如何有效的降低維護(hù)測試程式的成本?
PS:透過一些Testable Design Pattern 可以有效提升可維護(hù)性。例如: Repository Pattern, Service Pattern……
3、測試代碼的可讀性
1)你的測試程式的命名是否易于理解?
2)當(dāng)你測試失敗時是否能從測試失敗的測試方法(TestMethod)明確看出實際失敗的原因?
3)當(dāng)讀取測試數(shù)據(jù)的人看不懂你的測試,人們就不會執(zhí)行這些測試、也不會去維護(hù)這些測試,久而久之就會越來越惡化。
Test Driven Development & Unit Test
寫在本文最后,其實我一直覺得單元測試其實是為了TDD開發(fā)模式而誕生的,在這種開發(fā)模式下使用單元測試完全是非常順暢的:
1、根據(jù)軟件需求文檔拆解軟件功能,并設(shè)計出功能模塊劃分;
2、根據(jù)需要的功能模塊設(shè)計出單元測試場景用例,因為此時可以很清晰的知道能夠提供什么樣的數(shù)據(jù),以及需要達(dá)到什么樣的功能,這對設(shè)計單元測試用例已經(jīng)完 全足夠了;
3、編寫單元測試代碼,這個時候可以專注于檢驗這個方法的是否滿足設(shè)計的要求,此時甚至實際的代碼還根本沒開發(fā),而.NET 4.0的Dynamic關(guān)鍵字在這里可以得到充分的發(fā)揮:調(diào)用那些根本都還不存在的方法,卻不會導(dǎo)致編譯無法通過。
4、若在編寫單元測試過程中,可以預(yù)期當(dāng)前這個方法若需要調(diào)用一些其他類或方法的支持,可以通過編寫Mock Object來模擬,同樣也是無需實現(xiàn)真正的代碼,只需要有基本的代碼框架或者接口即可。
5、在為這個方法編寫好單元測試代碼之后,就可以開始編寫實際的代碼實現(xiàn)了,因為在之前為了滿足Testability的需要,代碼已經(jīng)是基于依賴倒置模 式的了,無需再擔(dān)心其他需要調(diào)用的類或方法是否已經(jīng)實現(xiàn)或正確實現(xiàn)。在編寫好本方法的實現(xiàn)之后就可以通過運行之前的單元測試進(jìn)行驗收了。
可以看到,若按照以上這種方式進(jìn)行開發(fā),首先代碼的耦合性是非常低的,其次代碼的質(zhì)量也是很高的,最后還會因為代碼之間的耦合度低從而降低在開發(fā)過程中, 相互制約進(jìn)度相互影響的可能性。在追查BUG的時候也很有優(yōu)勢:很容易查到BUG是否蔓延。
反之,對一個Legacy System進(jìn)行重構(gòu)使之Testable,再編寫單元測試其實工作量不小,實際的收益也不會特別大。
單元測試的基本概念以及價值就基本講完,下篇文章將開始介紹Visual Studio 2010中的單元測試工具與環(huán)境。
做單元測試的過程,我們興奮過,低落過,冷靜過,改革過,現(xiàn)在良性發(fā)展中,剛開始做的時候很興奮,一天只知道寫啊寫啊,到頭來寫了很多單元測試用例,但到現(xiàn)在扔掉的也很多。
當(dāng)然原因也很多,主要還是測試目的的不明確,測試范圍的不明確,測試標(biāo)準(zhǔn)不明確,對模塊本向在產(chǎn)品中為何存在,為什么是這樣存在沒有弄清楚,走了很多灣路。
當(dāng)然接到一個模塊的測試任務(wù)時,應(yīng)該與開發(fā)人員,系統(tǒng)測試人員和產(chǎn)品人員,了觖這個模塊為什么存在,有什么功能,在產(chǎn)品中是怎么樣的一個位置和作用。
要多讀開發(fā)的代碼,測試用例寫完后,一定要調(diào)試一次測試案例,跟到開發(fā)的代碼里面去,一行一行的看狀態(tài)是不是正確,一般都會發(fā)現(xiàn)很多bug的。
測試一個模塊不光要看是否正確實現(xiàn)了想要的功能,還要看開發(fā)這樣寫是不是合理,有沒有改進(jìn)的地方,并提出改進(jìn)方案,開心的就是我們周圍的一群同事常常在測試一個模塊的時候就會寫一封改進(jìn)方案的郵件發(fā)給開發(fā)人員,并抄送給相關(guān)上級,在算法的簡單,性能的提高,成本的減少都引到了很好的作用。這也是人們的開發(fā)人員很尊重我們測試人員提出的任務(wù)意見的原因,原因他們知道這個模塊還有一個測試人員和他們自己一樣對這個模塊里的每一行代碼都很熟悉。
測試的標(biāo)準(zhǔn)很難,怎么樣才算這個模塊測試好了,因為上級不用看你寫的測試用例,他只要數(shù)據(jù),但是給什么他才有說服力,我們在案例數(shù),bug數(shù)等很多數(shù)據(jù)里選了代碼覆蓋率做為標(biāo)準(zhǔn),雖然這個數(shù)據(jù)也不能充分說明測試的完整性,但相對來說還是比較有信服性。
測試用例在后期維護(hù)的時候,我們就頭疼過,而且是很頭疼,模塊內(nèi)部實現(xiàn)變了,接口變了,在產(chǎn)品里的位置變了,數(shù)據(jù)結(jié)構(gòu)變了,我們的用例就天天跟著開發(fā)也在變。目前沒有特別好的辦法,只能看具體問題具體處理,在寫用例的時候還是有一點點技巧的。
可恨的setup和teardown,之前對它的理解就是測試前的事情和測試后做的事情,所以不太注意這個地方,就引起測試用例一個一個運行是正常的,一批一批運行就失敗了;一個人寫的案例運行是正常的,兩個寫的案例在一起運行就失敗了。一失敗就是幾百個,要找出是那個用例引起的測試環(huán)境的破壞,很痛苦。真的很痛苦?。。?#8220;吃掉自己的狗食”這句話用到這里絕對合適,不要僅把自己的setup做好,自己的teardown比setup更重要。
測試用例常失敗的幾個原因:
1、行為敏感性 如果系統(tǒng)的行為發(fā)生變化,如需求變了,這時測試就會失敗。
2、接口敏感性
3、數(shù)據(jù)敏感性測試
4、上下文敏感。
突然想寫,寫的比較亂多見涼。
本文討論的是基于字符終端型的銀行核心業(yè)務(wù)系統(tǒng)。銀行核心業(yè)務(wù)系統(tǒng)由于其復(fù)雜的業(yè)務(wù)流程,以及特殊的終端字符形式,與一般的B/S結(jié)構(gòu)、C/S結(jié)構(gòu)系統(tǒng)有較大的差異,其性能測試方法也存在很多的不同。下面就我對銀行核心業(yè)務(wù)系統(tǒng)的理解,以及利用LoadRunner進(jìn)行銀行核心業(yè)務(wù)系統(tǒng)性能測試的相關(guān)經(jīng)驗,介紹一下有關(guān)銀行核心業(yè)務(wù)系統(tǒng)的性能測試內(nèi)容、測試方法,以及測試過程中的注意事項等。
測試內(nèi)容
◆ 聯(lián)機(jī)業(yè)務(wù)
聯(lián)機(jī)業(yè)務(wù)主要是有關(guān)的柜臺業(yè)務(wù),如卡/折業(yè)務(wù)、貸款業(yè)務(wù)等。對聯(lián)機(jī)業(yè)務(wù)的測試,主要是關(guān)注以下內(nèi)容:
● 不同并發(fā)用戶數(shù)(終端數(shù))下,核心業(yè)務(wù)系統(tǒng)的處理能力,包括交易數(shù)/秒、交易成功率等;
● 不同并發(fā)用戶數(shù)(終端數(shù))下,各服務(wù)器端的資源利用情況,如數(shù)據(jù)庫服務(wù)器、應(yīng)用服務(wù)器、前置服務(wù)器端的CPU利用率、可用內(nèi)存等;
● 不同并發(fā)用戶數(shù)(終端數(shù))下,各常用交易的響應(yīng)時間情況;
● 一定并發(fā)用戶數(shù)(終端數(shù))下,系統(tǒng)長期運行的穩(wěn)定性等。
◆ 批量業(yè)務(wù)
批量業(yè)務(wù)主要是結(jié)息相關(guān)的批處理業(yè)務(wù)。對批量業(yè)務(wù)的測試,主要是關(guān)注以下內(nèi)容:
● 結(jié)息的賬戶數(shù),包括活期戶數(shù)、卡數(shù)、一本通數(shù)、貸款戶數(shù)等;
● 結(jié)息的時間,包括起始時間、終止時間。
● 特殊交易
特殊交易主要是指日始簽到、日終軋賬等業(yè)務(wù)。對特殊交易的測試,主要是關(guān)注以下內(nèi)容:
● 不同并發(fā)用戶數(shù)(終端數(shù))下,核心業(yè)務(wù)系統(tǒng)對日始簽到、日終軋賬的處理能力,包括登錄柜員數(shù)/秒、軋賬柜員數(shù)、登錄成功率、軋賬成功率等;
● 不同并發(fā)用戶數(shù)(終端數(shù))下,日始簽到、日終軋賬時各服務(wù)器端的資源利用情況,如數(shù)據(jù)庫服務(wù)器、應(yīng)用服務(wù)器、前置服務(wù)器端的CPU利用率、可用內(nèi)存等;
● 不同并發(fā)用戶數(shù)(終端數(shù))下,日始簽到、日終軋賬的響應(yīng)時間情況。
測試方法
◆ 聯(lián)機(jī)交易
● 對核心業(yè)務(wù)系統(tǒng)進(jìn)行負(fù)載測試
按照確定的測試功能及用戶分布情況,模擬不同數(shù)量的柜員并發(fā)執(zhí)行聯(lián)機(jī)交易,得到各執(zhí)行交易的響應(yīng)時間、每秒的交易數(shù)、成功的交易數(shù)、失敗的交易數(shù),以及各服務(wù)器的CPU、內(nèi)存利用情況等。
● 對核心業(yè)務(wù)系統(tǒng)進(jìn)行疲勞測試
按照確定的測試功能及用戶分布情況,模擬一定數(shù)量的柜員并發(fā)執(zhí)行聯(lián)機(jī)交易,連續(xù)運行一段時間,得到成功的交易數(shù)、失敗的交易數(shù),以及各服務(wù)器的CPU、內(nèi)存利用情況等,從而得出系統(tǒng)長時間運行的穩(wěn)定性。
◆ 批量業(yè)務(wù)
啟動核心業(yè)務(wù)結(jié)息操作,利用軟件系統(tǒng)中的日志記錄功能,記錄驗證結(jié)息的開始時間和結(jié)束時間,得到核心系統(tǒng)對結(jié)息的處理效率。
◆ 特殊交易
● 日始簽到
模擬不同數(shù)量的柜員并發(fā)執(zhí)行簽到操作,記錄響應(yīng)時間、每秒的簽到柜員數(shù)、成功的簽到柜員數(shù)、失敗的簽到柜員數(shù),以及各服務(wù)器的CPU、內(nèi)存利用情況等。
● 日終軋賬
模擬不同數(shù)量的柜員并發(fā)執(zhí)行軋賬操作,記錄響應(yīng)時間、每秒的完成軋賬的柜員數(shù)、成功的軋賬柜員數(shù)、失敗的軋賬柜員數(shù),以及各服務(wù)器的CPU、內(nèi)存利用情況。
注意事項
軟件性能測試的大部份工作主要集中在測試前的準(zhǔn)備工作上,銀行核心業(yè)務(wù)系統(tǒng)也不例外,而且由于其特殊特點,準(zhǔn)備工作中還需要注意一些事項,如:
?。?)測試前需要準(zhǔn)備一定數(shù)量的數(shù)據(jù),為了保證不影響測試結(jié)果,需要在準(zhǔn)備數(shù)據(jù)中注意以下事項:
◆ 每個存折、卡賬號對應(yīng)的錢數(shù)足夠多,防止該賬號余額不足,而不能再連續(xù)進(jìn)行取款等操作,影響測試結(jié)果;
◆ 每個柜員對應(yīng)的尾箱的錢數(shù)足夠多,防止該柜員因為尾箱現(xiàn)金不夠而導(dǎo)致不能進(jìn)行取款、銷戶等操作,影響測試結(jié)果;
◆ 在非結(jié)息日事先選擇一些賬戶進(jìn)行存、取款業(yè)務(wù)操作,然后在結(jié)息日進(jìn)行結(jié)息操作。腳本準(zhǔn)備。
(2)利用LoadRunner準(zhǔn)備測試腳本過程中,需要根據(jù)核心業(yè)務(wù)系統(tǒng),選定終端類型、調(diào)整鍵盤選項等。
?。?)另外在測試場景運行前,需要在LoadRunner的Countroler中選中RTE用戶類型,否則虛擬用戶會一直處于掛起狀態(tài),LoadRunner默認(rèn)是沒有選中該用戶類型的。
以上對銀行核心業(yè)務(wù)系統(tǒng)的性能測試內(nèi)容、測試方法、測試中的注意事項等進(jìn)行了簡單的總結(jié),由于經(jīng)驗有限,而且銀行核心業(yè)務(wù)系統(tǒng)包含的內(nèi)容繁多,以上只是對一些簡單業(yè)務(wù)的性能測試進(jìn)行了討論,如有不當(dāng)之處,請批評指正。
自2010年06月21日中國人民銀行公布《非金融機(jī)構(gòu)支付服務(wù)管理辦法》以來,針對非金融機(jī)構(gòu)“支付業(yè)務(wù)許可證”的申請及檢測認(rèn)證工作已經(jīng)逐步展開。下面,我們將結(jié)合央行檢測認(rèn)證的相關(guān)規(guī)定,對非金融機(jī)構(gòu)第三方支付系統(tǒng)性能檢測的要點進(jìn)行解讀和分析。
一、第三方支付系統(tǒng)性能檢測內(nèi)容
中國人民銀行于2011年1月17日發(fā)布了《非金融機(jī)構(gòu)支付服務(wù)業(yè)務(wù)系統(tǒng)檢測認(rèn)證管理規(guī)定》(征求意見稿)。其對第三方支付系統(tǒng)性能檢測的目的和內(nèi)容作了如下說明:“驗證業(yè)務(wù)系統(tǒng)是否滿足業(yè)務(wù)需求的多用戶并發(fā)操作,是否滿足業(yè)務(wù)性能需求,評估壓力解除后的自恢復(fù)能力,測試系統(tǒng)性能極限”。
通過這段說明我們不難看出,對支付服務(wù)業(yè)務(wù)系統(tǒng)性能的檢測主要包括以下三方面內(nèi)容:一是系統(tǒng)的并發(fā)能力驗證;二是壓力解除后系統(tǒng)自恢復(fù)能力;三是系統(tǒng)性能極限驗證。
系統(tǒng)的并發(fā)能力驗證應(yīng)包含兩方面檢測內(nèi)容:一是驗證系統(tǒng)是否支持業(yè)務(wù)的多用戶并發(fā)操作;二是結(jié)合典型交易檢驗各測試點在給定并發(fā)用戶數(shù)下,系統(tǒng)各項性能指標(biāo)是否滿足用戶性能需求。
系統(tǒng)自恢復(fù)能力驗證的內(nèi)容主要是在系統(tǒng)并發(fā)能力驗證和系統(tǒng)性能極限驗證的同時,記錄各測試點在加壓和壓力解除前后系統(tǒng)資源的使用情況及資源恢復(fù)所用的時間。
系統(tǒng)性能極限驗證的內(nèi)容主要是對典型交易采用極限測試策略,通過逐步增加系統(tǒng)負(fù)載的方式,測試系統(tǒng)性能的變化,并最終確定在什么負(fù)載條件下系統(tǒng)性能處于失效狀態(tài),同時記錄此時系統(tǒng)所能承受的最大并發(fā)用戶數(shù)。
二、第三方支付系統(tǒng)性能檢測要點分析
與其他應(yīng)用系統(tǒng)的性能測試一樣,規(guī)范的第三方支付系統(tǒng)性能測試同樣需要經(jīng)歷測試準(zhǔn)備、測試實施和測試總結(jié)等過程。
1) 性能需求分析
因各家非金融機(jī)構(gòu)支付服務(wù)系統(tǒng)的用戶規(guī)模不同,所以央行并未對第三方支付系統(tǒng)性能檢測環(huán)境和性能指標(biāo)進(jìn)行硬性規(guī)定,性能指標(biāo)的確認(rèn)依據(jù)主要來自于系統(tǒng)需求文檔中對性能的約定或用戶性能需求的調(diào)研。
性能需求的主要調(diào)查內(nèi)容包括:系統(tǒng)實際使用的用戶數(shù)量、正常情況下系統(tǒng)的平均使用用戶數(shù)、高峰時段的在線用戶量、可預(yù)期生命周期內(nèi)系統(tǒng)的用戶增長情況、一年的業(yè)務(wù)量及日交易量、壓力解除后系統(tǒng)自恢復(fù)時間要求等。
2) 測試策略分析
根據(jù)非金融機(jī)構(gòu)支付服務(wù)系統(tǒng)的業(yè)務(wù)特點,對其性能的測試大致可分為兩類:一類是包含數(shù)據(jù)插入操作和數(shù)據(jù)查詢操作的并發(fā)測試性能(如:支付、交易明細(xì)查詢等);另一類是大數(shù)據(jù)量處理性能(如:日終批處理等)。
并發(fā)測試策略的主要內(nèi)容應(yīng)包括:并發(fā)用戶數(shù)、性能指標(biāo)要求(包括響應(yīng)時間、系統(tǒng)資源占用)等;對大數(shù)據(jù)量計算性能測試策略的制定過程中,需要關(guān)注的是對批處理交易數(shù)據(jù)量的要求。
3) 性能測試點選取分析
按照央行的定義,第三方支付服務(wù)包含網(wǎng)絡(luò)支付、預(yù)付卡和銀行卡收單等,而無論采用哪種支付方式,三種支付平臺實質(zhì)上都是買賣雙方交易過程中的“中間件”,它的核心功能就是通過提供的支付網(wǎng)關(guān)為交易雙方提供支付、充值等交易服務(wù),并記錄雙方的交易數(shù)據(jù)。對其測試點的選擇可以典型交易、復(fù)雜業(yè)務(wù)流程、頻繁的用戶操作、大數(shù)據(jù)量處理等為總體指導(dǎo)原則,圍繞支付、交易管理、資金結(jié)算、對賬處理等核心業(yè)務(wù)進(jìn)行選取。
在網(wǎng)絡(luò)支付系統(tǒng)中,我們將重點選取支付、預(yù)存、交易明細(xì)查詢、日終批處理等操作進(jìn)行測試;預(yù)付卡部分重點選取聯(lián)機(jī)消費、聯(lián)機(jī)余額查詢、交易明細(xì)查詢、批量充值、日終批處理等操作進(jìn)行測試;銀行卡收單部分重點選取消費、預(yù)授權(quán)、日終批處理等操作進(jìn)行測試。
三、第三方支付系統(tǒng)測試方法簡析
第三方支付系統(tǒng)性能測試可以選擇常見的商用性能測試軟件進(jìn)行,但需要注意的是由于交易過程通常需要調(diào)用銀行接口與協(xié)約銀行進(jìn)行數(shù)據(jù)交換,因此在測試腳本編輯過程中需要用模擬接口來替換真實的銀行接口來測試支付平臺的真實性能。預(yù)付卡和銀行卡收單其交易數(shù)據(jù)的來源均為Pos機(jī),性能測試中只能用開發(fā)的工具或編制的腳本來模擬發(fā)送報文到Pos前置服務(wù)器進(jìn)行并發(fā)測試,具體可通過Socket協(xié)議編寫報文發(fā)送腳本的過程進(jìn)行實現(xiàn)。
每個公司都有自己的基因。做產(chǎn)品起家的,和網(wǎng)絡(luò)公司不同。對于性能測試,很多的思維還停留在單機(jī)時代。于是很多QA就認(rèn)為,無非就是測試CPU,memory,disk等參數(shù)而已。
但是隨著后臺的service程序逐漸增多,service的性能測試,和之前測試一個產(chǎn)品,已經(jīng)有了很多的不同。這里,就談?wù)勥@次性能測試的一些經(jīng)驗。
其實之前QA已經(jīng)做過了一些性能測試。但是有一天我們計劃購入機(jī)器為產(chǎn)品上線做準(zhǔn)備,manager問我如何購買機(jī)器。我一看module不少,首先就考慮如何分配這些moudule在不同的機(jī)器上,以獲得最好的性能。于是我要搞清楚每個module的性能瓶頸,到底是cpu bound,還是IO bound,還是memory bound。
我就建議QA做了這樣的測試。測試結(jié)果出來了。從結(jié)果看來,掃描病毒的模塊CPU還是一個瓶頸,畢竟,掃描病毒是一個很耗時的操作。而web service則需要較多的memory。
我后面關(guān)心的就是那么多模塊協(xié)同工作,誰是最慢的環(huán)節(jié)。因為我們之前的設(shè)計還是考慮的拓展性,所以,對于最慢的環(huán)節(jié),通過增加進(jìn)程數(shù)目和增加機(jī)器可以改善。
結(jié)果出來了,QA很快就根據(jù)他們的性能,給出了一組最小的機(jī)器配置列表。哪些module可以放在一起,每個module至少要起幾個進(jìn)程才不至于出現(xiàn)特別慢的module block整個service的效率。這下就簡單了。我們可以把它作為一個service組,以后增加機(jī)器,就按照這樣的配置成倍的增加。
其實這樣的思路,就是現(xiàn)在所謂的SOA運維。別人問你需要多少機(jī)器,如何擴(kuò)容,你給出的不是幾臺機(jī)器,而是以一個最小的service集群組為單位的系統(tǒng)配置。比如說你的service有3個模塊,他們的配置可能是
模塊 數(shù)目 特征
A 1 CPU bound
B 2 IO bound
C 1 memory bound
意味這增加一個A和C,需要兩個B配合。
這樣,最小的配置就是 1 cpu,2 disk,1 memory,有可能對于到服務(wù)器上面,就是
8core cpu
15000 PRM SAS disk *2
32G memory
有了這樣的最小單位,下面才是真正的性能測試環(huán)節(jié),我們要知道,這個最小的service單位,能夠有多大的吞吐量。
于是,盡可以多地喂數(shù)據(jù),看看輸出的效率。這時,我們關(guān)心的已經(jīng)不是cpu、memory、io這些參數(shù)了,因為你的最小配置必須是這樣的。你可能會浪費CPU,浪費內(nèi)存,但是沒有辦法,因為瓶頸在那里,要增加機(jī)器提高整體吞吐量,IO是瓶頸。 我們關(guān)心的是,整個最小的service集合到底能有多大的吞吐量。如果我要更大的吞吐量,需要多少個這樣的service單位。
這樣的性能測試結(jié)果,對產(chǎn)品,對運維才是真正有意義的。這就是從整體的角度去考慮一個service產(chǎn)品。而這也為RD后期的開發(fā)起了指導(dǎo)意義,哪個模塊是重頭戲,對整體而言起決定意義。需要重點調(diào)優(yōu),哪個模塊雖然效率很低,但是調(diào)優(yōu)的優(yōu)先級可以放低。因為他不是關(guān)鍵。
這里的關(guān)鍵,就在于強(qiáng)調(diào)整體測試。而且這個整體是建立在之前模塊測試后的模塊配比的基礎(chǔ)上的。強(qiáng)調(diào)最小service集合的測試。
百無聊懶中參加了“進(jìn)銷存系統(tǒng)”的測試工作,發(fā)現(xiàn)自己還真是喜歡測試,拿起一個軟件就想用用,想測測。
測試該系統(tǒng)僅用了差不多一天的時間,沒有寫測試用例,因為該系統(tǒng)是公司內(nèi)部使用,所以應(yīng)上級要求,作為測試人員我只是測試了下基本的功能,對于一些輸入框什么的,沒有考慮一些特殊的輸入,比如特殊字符等。
感覺收獲還是有的,現(xiàn)在總結(jié)下心得:
一、首先要進(jìn)行單元功能測試,保證每個功能使用的正確
有以下幾個問題,需要自己以后測試中注意:
1、重復(fù)添加問題:某個產(chǎn)品被刪除或設(shè)為“未激活”狀態(tài)后,再次添加同樣的產(chǎn)品信息,能否添加上?或者添加已經(jīng)存在的產(chǎn)品信息,系統(tǒng)是否有相應(yīng)的提示信息?
2、采購單或送貨單中,產(chǎn)品的刪除問題:是否能夠連續(xù)刪除多個產(chǎn)品?當(dāng)采購單或送貨單重只有一個產(chǎn)品時,能否刪除掉?
3、產(chǎn)品重量是否可以改為小數(shù)?
4、出庫管理模塊,匯入/匯出功能,要考慮到導(dǎo)入正確的數(shù)據(jù)、誤寫的數(shù)據(jù)、不存在的數(shù)據(jù)三種情況,但導(dǎo)入這些數(shù)據(jù)時,系統(tǒng)是否有錯誤提示信息?
二、業(yè)務(wù)流程的測試
1、各個模塊間數(shù)據(jù)傳遞是否正確,要注意到“重量”和“金額”數(shù)值類型的問題,比如,當(dāng)重量和金額都有兩位小數(shù)時,傳到其他模塊是否數(shù)值一致?
模塊間數(shù)據(jù)傳遞比較復(fù)雜,一定得著重測試,凡是涉及到模塊間數(shù)據(jù)傳遞的一定要設(shè)計不同的數(shù)據(jù)進(jìn)行測試,這往往是開發(fā)最容易出錯的地方。
2、采購后相應(yīng)的產(chǎn)品庫存是否相應(yīng)的增加?
3、出庫后相應(yīng)的產(chǎn)品庫存是否相應(yīng)的減少?
三、權(quán)限分配
1、以不同權(quán)限登錄時,信息顯示是否正確?
2、以不同權(quán)限登錄進(jìn)行操作,能否操作成功?同樣要注意數(shù)據(jù)傳遞關(guān)系。
四、個人對該進(jìn)銷存系統(tǒng)的認(rèn)識
該系統(tǒng)和我想象中的不太一樣,它是以訂單為驅(qū)動來開發(fā)的,包括:
產(chǎn)品管理:主要是錄入產(chǎn)品的一些基本信息
訂單管理:該模塊比較復(fù)雜,以訂單為驅(qū)動,下采購訂單,在采購訂單的基礎(chǔ)上生成送貨單,入庫,涉及到了批次入庫(生成批號并填寫有效日期)
出庫管理:遵循先進(jìn)先出的原則,即,批號小的先出。
我發(fā)現(xiàn)很多人,包括論壇上的網(wǎng)友,還有很多身邊的同事都對UI自動化充滿了一些恐懼感,從而不敢觸及它。當(dāng)然也有一定的原因是覺得UI自動化沒太深的技術(shù)含量,這也是我討厭UI自動化的唯一原因。但是,一旦讓這些人去做UI自動化的話,是很難做好的,因為UI自動化需要一定的經(jīng)驗,而我個人認(rèn)為一年的經(jīng)驗,一個正規(guī)的項目應(yīng)該都能具備編寫良好UI自動化測試的能力。因此,對于后來的人,我想把UI自動化關(guān)鍵的幾條再談一談,UI自動化確實沒什么技術(shù)含量,你掌握了以下幾點也能成為一個小專家了。
1. 用高級語言編寫自動化程序,在UI的部分調(diào)用UI自動化工具。我反對純用UI自動化工具去寫自動化,因為那樣就太死板了,而且功能不強(qiáng)大,不靈活。我推薦學(xué)好一門高級語言,把大多數(shù)的自動化都用這門高級語言實現(xiàn),只在需要UI操作的時候才調(diào)用UI工具。
2. 只在你測試的UI模塊上進(jìn)行自動化的測試,其他地方避免用UI去操作,使用高級語言去實現(xiàn)。這樣你需要用UI的地方就進(jìn)行了最小化,從而使得只有在真正需要UI的地方才自動化UI,因此測試程序會相對更穩(wěn)定。
3. UI自動化最基本的操作就是發(fā)現(xiàn)控件和操作控件。盡量避免用text來發(fā)現(xiàn)控件,而使用一些固定的控件屬性來發(fā)現(xiàn),比如Control ID等等。這樣的話,測試程序會更穩(wěn)定,開發(fā)改變文本不會影響到你,而你也不用擔(dān)心localization的問題。
4. 操作控件分為模擬用戶操作和事件驅(qū)動。簡單的例子就是,模擬用戶操作就是鼠標(biāo)真的去點一下,而事件驅(qū)動則是跳過點擊直接引發(fā)點擊的事件。我以前用過具有這種功能的工具,但是最近幾年用的工具不具備這個功能。
5. 解決好同步問題。UI自動化最不穩(wěn)定的地方就是同步問題了,你不能連續(xù)點擊,而需要等待到一定的情況才能進(jìn)行下一次點擊。各種情況都不太一樣,需要一些經(jīng)驗進(jìn)行良好的程序設(shè)計。但是,簡單來講,要做到等待的情況發(fā)生能立刻返回到程序,不能空等。
6. 減少其他UI對你自動化程序的影響,比如關(guān)閉Windows balloon,等等。一般來說是發(fā)現(xiàn)了有其他UI影響你的情況,就想一下workaround, 不會有什么大問題。
從我的經(jīng)驗上來看,一般UI自動化有問題都能歸結(jié)于以上幾點,而一旦你解決了以上幾點的話,UI自動化就變成了一個熟練工的工作了,沒什么挑戰(zhàn)性。我本人的有些模塊的UI自動化基本可以達(dá)到100%的通過率,而所有模塊的自動化也能達(dá)到95%以上的通過率。不過我基本已經(jīng)脫離UI自動化了,因為太沒有技術(shù)含量了,不過我還是認(rèn)為如果你剛剛進(jìn)入測試的工作,或者從來沒有接觸過UI自動化,或者從來都沒有做好過UI自動化的話,在這上邊工作個2,3年會有一定的收獲的。
控件類型 | 大分類 | 小分類 | 檢查內(nèi)容 | 結(jié)果判定 |
TextBox | 數(shù)值型 | 邊界值 | 輸入[最小值-1] | 程序應(yīng)提示錯誤 |
輸入[最小值] | OK |
輸入[最大值] | OK |
輸入[最大值+1] | 程序應(yīng)提示錯誤 |
位數(shù) | 輸入[最小位數(shù)-1] | 程序應(yīng)提示錯誤 |
輸入[最小位數(shù)] | OK |
輸入[最大位數(shù)] | OK |
輸入[最大位數(shù)+1] | 程序應(yīng)提示錯誤 |
允許輸入小數(shù)位的控件,小數(shù)位的長度做以上同樣測試 | 同上 |
異常值、特殊值 | 輸入[空白(NULL)]、空格或‘“~!@#$%^&*()_+-={}[]|\:;”’<>,./?;”等可能導(dǎo)致系統(tǒng)錯誤的字符 | 程序應(yīng)提示錯誤 |
禁止直接輸入特殊字符時,使用“粘貼”、“拷貝”功能嘗試輸入,并測試能否正常提交保存。 | 只能使用“粘貼”、“拷貝”方法輸入的特殊字符應(yīng)無法保存,并應(yīng)給出相應(yīng)提示 |
word 中的特殊功能,通過剪貼板拷貝到輸入框:分頁符,分節(jié)符,類似公式的上下標(biāo)等 | 程序應(yīng)提示錯誤 |
輸入[負(fù)值] | 根據(jù)設(shè)計書要求判定 |
輸入設(shè)計書中明確指出禁止輸入的數(shù)字 | 根據(jù)設(shè)計書要求判定 |
輸入[英文字母] | 程序應(yīng)提示錯誤 |
數(shù)值輸入的長度:整型----32位 最大值 65535,最小值-65535;16位 最大值 32767,最小值-32767 | 根據(jù)設(shè)計書要求判定 |
帶符號的數(shù)值:帶正號的正數(shù),帶負(fù)號的負(fù)數(shù) | 根據(jù)設(shè)計書要求判定 |
小數(shù):小數(shù)點后的位數(shù),小數(shù)的四舍五入問題,小數(shù)點前零舍去的情況,如 .12;多個小數(shù)點的情況;0值:0.0,0.,.0 | 根據(jù)設(shè)計書要求判定 |
分?jǐn)?shù):如 2/3 | 根據(jù)設(shè)計書要求判定 |
首位為零的數(shù)值:如01=1 | 根據(jù)設(shè)計書要求判定 |
科學(xué)技術(shù)法是否支持:如 1.0E2 | 根據(jù)設(shè)計書要求判定 |
指數(shù)是否支持 | 根據(jù)設(shè)計書要求判定 |
全角數(shù)字和半角數(shù)字的情況 | 根據(jù)設(shè)計書要求判定 |
數(shù)字與字母的混合:16進(jìn)制數(shù)值,8進(jìn)制數(shù)值 | 根據(jù)設(shè)計書要求判定 |
貨幣型輸入項:允許小數(shù)點后幾位 | 根據(jù)設(shè)計書要求判定 |
字符型 | 字符種類 | 輸入[全角字符] | 根據(jù)設(shè)計書要求判定 |
輸入[半角字符] | 根據(jù)設(shè)計書要求判定 |
數(shù)字字符 | 根據(jù)設(shè)計書要求判定 |
郵政編碼輸入項的輸入限制,如只能輸入半角數(shù)字字符或某幾個指定字符 | 根據(jù)設(shè)計書要求判定 |
電話號碼和傳真輸入限制,如只能輸入半角數(shù)字字符和半角括號“()”及半角減號“-”;電話或傳真只能輸入數(shù)字和減號。 | 根據(jù)設(shè)計書要求判定 |
E-mail地址的格式檢查,如輸入字符串中必須包含“@”和半角“.”字符。 | 根據(jù)設(shè)計書要求判定 |
年齡的輸入限制檢查,一般<=200即可。 | 根據(jù)設(shè)計書要求判定 |
輸入設(shè)計書中明確指出禁止輸入的字符 | 程序應(yīng)提示錯誤 |
輸入[空白(NULL)]或“~!@#$%^&*()_+-={}[]|\:;”’<>,./?;”等可能導(dǎo)致系統(tǒng)錯誤的字符 | 程序應(yīng)提示錯誤 |
密碼輸入項的特殊處理 | 登錄驗證時大、小寫是否區(qū)分 | 根據(jù)設(shè)計書要求判定 |
登錄只能輸入半角字符 | 根據(jù)設(shè)計書要求判定 |
是否允許輸入特殊字符 | 根據(jù)設(shè)計書要求判定 |
多行文本框輸入 | 允許回車換行 | 根據(jù)設(shè)計書要求判定 |
保存后再顯示能夠保持輸入時的格式 | 根據(jù)設(shè)計書要求判定 |
僅輸入回車換行,檢查能否正確保存;若能,查看保存結(jié)果。若不能,查看是否有正確提示 | 根據(jù)設(shè)計書要求判定 |
僅輸入空格,檢查能否正確保存;若能,查看保存結(jié)果。若不能,查看是否有正確提示 | 根據(jù)設(shè)計書要求判定 |
長度檢查 | 輸入[最小字符數(shù)-1] | 程序應(yīng)提示錯誤 |
輸入[最小字符數(shù)] | OK |
輸入[最大字符數(shù)] | OK |
輸入[最小字符數(shù)+1] | 程序應(yīng)提示錯誤 |
文件名輸入項的測試 | 輸入不存在的文件名 | 程序應(yīng)提示錯誤 |
輸入文件名稱超長(256個字符) | 程序應(yīng)提示錯誤 |
輸入帶路徑的文件名和不帶路徑的文件名 | 根據(jù)設(shè)計書要求判定 |
手工輸入后綴名稱 | 根據(jù)設(shè)計書要求判定 |
對于文件大小的限制,需要采用邊界值法測試系統(tǒng)的處理方式是否符合需求;考慮磁盤空間不足/滿的情況 | 程序應(yīng)提示錯誤 |
文件名的非法字符集:/\:*?"<>| | 程序應(yīng)提示錯誤 |
不輸入文件名和輸入空格 | 程序應(yīng)提示錯誤 |
輸入中間有空格的路徑名和文件名 | 根據(jù)設(shè)計書要求判定 |
輸入合法字符,但影響系統(tǒng)判斷文件名有效性的情況,如輸入a;b-20003.5.8 | 根據(jù)設(shè)計書要求判定 |
日期型 | 合法性檢查 | 日輸入[0日] | 程序應(yīng)提示錯誤 |
日輸入[1日] | OK |
日輸入[32日] | 程序應(yīng)提示錯誤 |
月輸入[1、3、5、7、8、10、12月]、日輸入[31日] | OK |
月輸入[4、6、9、11月]、日輸入[30日] | OK |
月輸入[4、6、9、11月]、日輸入[31日] | 程序應(yīng)提示錯誤 |
輸入非閏年,月輸入[2月]、日輸入[28日] | OK |
輸入非閏年,月輸入[2月]、日輸入[29日] | 程序應(yīng)提示錯誤 |
(閏年)月輸入[2月]、日輸入[29日] | OK |
(閏年)月輸入[2月]、日輸入[30日] | 程序應(yīng)提示錯誤 |
月輸入[0月] | 程序應(yīng)提示錯誤 |
月輸入[1月] | OK |
月輸入[12月] | OK |
月輸入[13月] | 程序應(yīng)提示錯誤 |
異常值、特殊值 | 輸入[空白(NULL)]或“~!@#$%^&*()_+-={}[]|\:;”’<>,./?;”等可能導(dǎo)致系統(tǒng)錯誤的字符 | |
時間型 | 合法性檢查 | 時輸入[30時] | 允許輸入30時制的項目“OK"; 不允許輸入30時制的項目程序應(yīng)提示錯誤 |
時輸入[31時] | 程序應(yīng)提示錯誤 |
時輸入[00時] | 程序應(yīng)提示錯誤 |
30時制是否允許存在1點~5點 | ?? |
分輸入[59分] | OK |
分輸入[60分] | 程序應(yīng)提示錯誤 |
分輸入[00分] | OK |
秒輸入[59秒] | OK |
秒輸入[60秒] | 程序應(yīng)提示錯誤 |
秒輸入[00秒] | OK |
異常值、特殊值 | 輸入[空白(NULL)]或“~!@#$%^&*()_+-={}[]|\:;”’<>,./?;”等可能導(dǎo)致系統(tǒng)錯誤的字符 | 程序應(yīng)提示錯誤 |
特定值(如:只允許輸入:"0","1"等) | 合法性檢查 | 分別輸入所有允許輸入的特定值 | OK |
輸入任意不屬于特定值范圍的字符 | 程序應(yīng)提示錯誤 |
異常值、特殊值 | 輸入[空白(NULL)]或“~!@#$%^&*()_+-={}[]|\:;”’<>,./?;”等可能導(dǎo)致系統(tǒng)錯誤的字符 | 程序應(yīng)提示錯誤 |
ChcecBox | 復(fù)選 | 連續(xù)選擇 | 連續(xù)選擇相鄰的checkbox | OK |
跳躍選擇 | 跳躍選擇不連續(xù)的checkbox | OK |
ComboBox | 單選 | | 選擇某一個列表項 | 被選中項目高亮或底色顯示 |
復(fù)選 | | 使用ctrl選擇多個列表項 | 根據(jù)設(shè)計書要求判定 允許多選時,所有被選中項目高亮或底色顯示; 不允許多選時,只有第一次被選中的項目高亮或底色顯示,再點擊其他項目應(yīng)無反應(yīng); |
0, 11, 92, 23, 0, 60, 93, 11 Bitmap | 鼠標(biāo)操作 | 上鍵頭 | 鼠標(biāo)點擊按件的“上箭頭” | text框中數(shù)量自動+1 |
下鍵頭 | 鼠標(biāo)點擊按件的“下箭頭” | text框中數(shù)量自動-1 |
鍵盤操作 | 上鍵頭 | 按下鍵盤的“上箭頭” | text框中數(shù)量自動+1 |
下鍵頭 | 按下鍵盤的“下箭頭” | text框中數(shù)量自動-1 |
箭頭控制輸入值 | 邊界值 | 輸入[最小值-1] | 程序應(yīng)提示錯誤 |
輸入[最小值] | OK |
輸入[最大值] | OK |
輸入[最大值+1] | 程序應(yīng)提示錯誤 |
text框輸入值 | 同TextBox輸入測試
|
最近還是發(fā)現(xiàn)有一些文章,個人對于自動化測試報有很大的懷疑態(tài)度,本人也對相關(guān)的文章給與了駁斥。我個人和公司對自動化測試都是報有很積極的態(tài)度的。這里我想再次的寫一篇文章來闡述到底UI自動化測試可以做什么,作為一個優(yōu)秀的UI自動化測試工程師應(yīng)該具備有什么方面的技能,以及本人對UI自動化的一些經(jīng)驗和體會。
首先還是要強(qiáng)調(diào)一點,API和command line程序都是非常適合用自動化來進(jìn)行測試的。我想這個觀點,即使那些反對自動化測試的人也不應(yīng)該否認(rèn)吧?至少我覺得他們應(yīng)該有這個意識。因此,對于他們反對自動化就集中在了UI自動化方面,我這里也完全站在UI自動化測試的角度來寫這篇文章,后邊就不再強(qiáng)調(diào)了。
再次套用朋友的一句話,"自動化測試聽起來很神秘,學(xué)起來很簡單,用起來很麻煩"。我想有過自動化測試經(jīng)驗的人,可能大多都有這個體會吧?前邊的過程我就不提了,以后主要探討為什么用起來會麻煩和怎樣簡單化自動化測試。總而言之,想搞好自動化測試,還是需要測試人員比較高的技術(shù)水平,尤其是編程能力和解決問題,分析問題的能力。
首先,我要談?wù)勛詣踊瘻y試工具和編程語言的關(guān)系。作為一個優(yōu)秀的自動化測試人員,他的最基本的能力就是編程水平了。所謂編程就是至少要精通一門高級語言,比如Java,C#等等,腳本語言不計算在內(nèi)。請記住,不是熟悉,是精通。高級編程語言給我們提供很強(qiáng)的能力來實現(xiàn)一些東西。你所精通的語言能力越強(qiáng),你在自動化測試可以做的事情就越多,越好。簡單來講,論程序的能力來排序是這樣的,測試工具-〉腳本語言-〉高級語言(Java,C#)-〉 C/C++-〉C++/CLI。根據(jù)這個語言能力的排序,結(jié)合你自己的語言能力,你可以想想你到底具備多少自動化測試能力。我所強(qiáng)調(diào)的是,如果你想優(yōu)秀,你至少要精通一門高級語言,這是必不可少的。如果很多人還只是用測試工具,腳本語言工作而抱怨自動化,我要強(qiáng)烈的建議他們好好去學(xué)習(xí)一下編程能力先了。他們的問題在于,由于編程能力的不足,使得自動化測試的很多問題沒有能力和辦法去解決。再談一下自動化測試工具,無論哪種測試工具,無論他們設(shè)計的多么強(qiáng)大,從編程語言來講,他們最多能夠達(dá)到腳本語言的能力。也就是說,如果你完全用測試工具來進(jìn)行自動化的開發(fā),很多問題你還是無法解決的。因此,我推薦的自動化開發(fā)方法是高級語言結(jié)合測試工具。我的自動化測試邏輯是,用測試工具只是完成UI操作,其他部分完全用高級語言來實現(xiàn)。我們不能否認(rèn)高級語言所具有的能力,他們創(chuàng)造出了世界上這么多豐富多彩,這么多優(yōu)秀的軟件,難道開發(fā)測試程序會有問題嗎?因此,我們的焦點就落在了測試工具的UI操作部分。
第二,關(guān)于測試工具。開發(fā)語言重要,選擇一個合適的測試工具也同樣的重要。一個靈活,強(qiáng)大的測試工具可以使你的自動化開發(fā)起到事半功倍的作用。結(jié)合不同的項目,不同的語言,你可能會有不同的選擇。不過,這里我想解釋的是,具有了高級語言的開發(fā)能力之后,我們期望測試工具來為我們做什么。我前邊也說過了,我們所要求自動化測試工具所做的就是UI的操作。這里邊比較重要的是三個方面,一是找到UI對象,二是操作UI對象,三是同步。如果一個工具能夠讓你找到所有的UI對象,并且能成功操作這些對象,就完全滿足我們的自動化開發(fā)需要了。如果,工具能夠提供同步的功能,就使你能夠如虎添翼,不然的話要自己去實現(xiàn),會麻煩不少。到了這里,你已經(jīng)具有了所有UI的操作能力(測試工具提供),并且具有了高級語言的實現(xiàn)能力(高級語言提供),你才有了基本的能力去做一個優(yōu)秀的自動化開發(fā)。沒有這些能力的人,我嚴(yán)重懷疑能否做出好的自動化測試。
第三,怎樣自動化。我的自動化的原則是,盡量少的進(jìn)行UI的操作,除非是你本身要測試的UI。道理很簡單,UI操作由于可能受各種問題的干擾,很容易失敗。通過非UI的方法去實現(xiàn)是更加可靠和快速的。這也是我為什么要強(qiáng)調(diào)對于高級語言的精通,具有高級語言的開發(fā)能力,你就能過把大量的任務(wù)從UI操作轉(zhuǎn)向了程序操作,使得你的自動化程序的可靠性大大的增強(qiáng)。這里還需要強(qiáng)調(diào)的一點能力就是系統(tǒng)應(yīng)用的能力,比如Windows使用的能力。Windows的很多的操作是有相關(guān)的命令來實現(xiàn)的,不一定非得通過大家熟悉的UI。記住這個原則:除非是你要測試的UI,否則盡可能的通過高級語言來實現(xiàn)。我想大家對于高級語言來實現(xiàn)的工作應(yīng)該還是有信心吧?因此,下邊我要談的內(nèi)容就完全的與你要測試的界面相關(guān)了。
第四,怎樣進(jìn)行UI測試。首先要盡量的減少UI操作,除非是你必須要測試的操作。比如簡潔快速的啟動你要測試的界面,用快捷鍵代替鼠標(biāo)操作等等。總而言之,理想狀態(tài)下我們進(jìn)行的每一次UI操作,都是我們需要測試的,其他操作盡量避免,不能避免用最可靠的方式去實現(xiàn)。那么我們現(xiàn)在的焦點就變成了,怎樣來處理我們真正要測試的UI了。UI測試的開發(fā)基本上就三個問題:發(fā)現(xiàn)對象,操作對象和同步。簡單解釋一下同步,同步就是有一個機(jī)制告訴你何時可以執(zhí)行一個 UI操作。很多人是用sleep的方式,等待一定的時間去執(zhí)行下一個操作,這是我非常反對的。我的原則是,盡量少用sleep,就算要用每次最多不要超過一秒。濫用sleep會嚴(yán)重影響測試程序的性能(具體的UI自動化過程,大家可以參考我的其他文章)。
第五,UI測試錯誤/異常的解決和Debug。通過以上的解釋,我們只是在自己需要測試的UI操作才進(jìn)行UI操作,否則通過高級語言或者系統(tǒng)命令來實現(xiàn)。是不是我們的UI自動化就完美了呢?絕對不是,這只是一個基礎(chǔ),還遠(yuǎn)遠(yuǎn)沒有達(dá)到完美。我們在自動化開發(fā)和應(yīng)用的過程中,大部分的時間其實是花費在了異常/錯誤處理和Debug上面。這跟真正的程序開發(fā)非常的類似,你如果去看代碼的話,大量的是在進(jìn)行返回值得檢驗和異常的處理。如果我們的程序在運行過程中出了問題怎么辦,或者如果沒有出現(xiàn)我們期望的結(jié)果怎么辦?一般來說有三種問題,第一是產(chǎn)品的問題,我們可以報bug了,第二是你測試程序的bug,你需要fix。第三是其他的問題,比如測試工具,甚至高級語言本身的問題,你需要workaround??偠灾?,優(yōu)秀的測試程序最終的目的是,一旦程序的運行發(fā)現(xiàn)了問題,就是產(chǎn)品的問題,就是可以報bug的。能夠達(dá)到這種境界才能算自動化測試的完美,才能算是一個真正優(yōu)秀的測試人員。(當(dāng)然了,正如軟件產(chǎn)品不可能沒有bug,你的測試程序也不可能完全沒有bug。但是,由于軟件產(chǎn)品是有大量的用戶來使用,而你的測試程序只是很小范圍內(nèi)來使用,使得你消除影響測試過程的bug成為完全可能)
綜上所述,一個優(yōu)秀的自動化測試工程師必須要具備高級語言的開發(fā)能力,自動化工具的靈活應(yīng)用能力,系統(tǒng)命令和使用的熟練能力等這些基本功,還更要具備優(yōu)秀的Debug,F(xiàn)ixbug的能力,和保持程序穩(wěn)定性能力。換句話講,一個優(yōu)秀的自動化測試工程師必定也是一個優(yōu)秀的軟件開發(fā)工程師。
最后談一下我為什么要轉(zhuǎn)向C++/CLI?從上邊的排序大家可以看到,C++/CLI是目前Windows平臺最強(qiáng)大的編程語言。在我的自動化開發(fā)的過程中,我需要高級語言和系統(tǒng)命令都不能完成的功能。如果沒有C++/CLI我就必須要通過UI來實現(xiàn),從而降低我程序的可靠性。而有了C++/CLI的功能,我就可以繞過UI操作了??傊?,能夠繞過UI操作的能力也體現(xiàn)出一個自動化測試人員的能力。從這個角度講,測試人員有很多東西要學(xué)的。最后說一下,我自動化工作的要求是100%可靠,我還不能完全滿足,因為使用我程序的人是那些手工測試的人,他們的使用環(huán)境的變化有可能引起一些問題的產(chǎn)生,基本上還不是我程序的問題,而是測試工具,或者其他模塊的問題,我需要想辦法去workaround。不過,隨著一定時間問題的積累和解決,如果環(huán)境不變,應(yīng)該可以達(dá)到100%可靠。(可是環(huán)境的變化是不會停止的,因此實際上很難達(dá)到永久的可靠,不過一段時間的可靠還是應(yīng)該可以達(dá)到的,或者說我們的測試開發(fā)必須有這樣一個目標(biāo),就如同軟件開發(fā)的目標(biāo)一樣)
什么是 Selenium?
Selenium 是 ThoughtWorks 專門為 Web 應(yīng)用程序編寫的一個驗收測試工具。據(jù) Selenium 主頁所說,與其他測試工具相比,使用 Selenium 的最大好處是:
“Selenium 測試直接在瀏覽器中運行,就像真實用戶所做的一樣。Selenium 測試可以在 Windows、Linux 和 MacintoshAnd 上的 Internet Explorer、Mozilla 和 Firefox 中運行。其他測試工具都不能覆蓋如此多的平臺。”
使用 Selenium 和在瀏覽器中運行測試還有很多其他好處。下面是主要的兩大好處:
* 通過編寫模仿用戶操作的 Selenium 測試腳本,可以從終端用戶的角度來測試應(yīng)用程序。
* 通過在不同瀏覽器中運行測試,更容易發(fā)現(xiàn)瀏覽器的不兼容性。
Selenium 的核心,也稱 browser bot,是用 JavaScript 編寫的。這使得測試腳本可以在受支持的瀏覽器中運行。browser bot 負(fù)責(zé)執(zhí)行從測試腳本接收到的命令,測試腳本要么是用 HTML 的表布局編寫的,要么是使用一種受支持的編程語言編寫的。
在下面的情況下,可以選擇SeleniumRC進(jìn)行功能測試。
* condition statements
* iteration
* logging and reporting of test results
* error handling, particularly unexpected errors
* database testing
* test case grouping
* re-execution of failed tests
* test case dependency
* screenshot capture of test failures
首先要下載SeleniumRC,不用安裝,解壓即可,可以看到這樣幾個目錄,下圖示:

selenium-server-1.0.1目錄,是服務(wù)器端,他可以接受測試程序指令,并將測試結(jié)果返回測試程序。
在測試前必須先啟動他,啟動過程:開始-運行-cmd-cd <服務(wù)器端目錄>-java -jar selenium-server.jar(服務(wù)器端其實就是個Jar文件)
然后就可以進(jìn)行客戶端,本文用C#來進(jìn)行測試,首先建立一個C#類庫工程,添加引用selenium-dotnet-client-driver-1.0.1目錄下的所有DLL,具體如下圖示。

下面,新建類SeleniumTest,具體代碼如下:
1 [TestFixture] 2 public class SeleniumTest 3 { 4 private ISelenium selenium; 5 private StringBuilder verificationErrors; 6 7 [SetUp] 8 public void SetupTest() 9 { 10 selenium = new DefaultSelenium("localhost", 4444, "*iexplore", "http://localhost:2896/WebTestSite/"); 11 selenium.Start(); 12 13 verificationErrors = new StringBuilder(); 14 } 15 16 [TearDown] 17 public void TeardownTest() 18 { 19 try 20 { 21 selenium.Stop(); 22 } 23 catch (Exception) 24 { 25 // Ignore errors if unable to close the browser 26 } 27 Assert.AreEqual("", verificationErrors.ToString()); 28 } 29 30 [Test] 31 public void TheSeleniumTest() 32 { 33 selenium.Open("/WebTestSite/"); 34 selenium.Type("TextBox1", "qeq"); 35 selenium.Type("TextBox2", "qwe"); 36 selenium.Click("Button1"); 37 38 //判斷是否出現(xiàn)alert("fail") 39 Assert.AreEqual("fail", selenium.GetAlert()); 40 41 selenium.Type("TextBox1", "123"); 42 selenium.Type("TextBox2", "123"); 43 selenium.Click("Button1"); 44 Assert.AreEqual("fail", selenium.GetAlert()); 45 46 //點擊鏈接 47 selenium.Click("link=2"); 48 //等待 49 selenium.WaitForPageToLoad("30000"); 50 selenium.Click("link=3"); 51 selenium.WaitForPageToLoad("30000"); 52 53 } 54 [Test] 55 public void TestTitle() 56 { 57 selenium.Open("/WebTestSite/**.aspx"); 58 Assert.AreEqual("yourtitle", selenium.GetTitle()); 59 60 } 61 } |
這樣,就建好了,可以打開NUit進(jìn)行測試,也可以直接寫個main進(jìn)行測試。
seleniumhq官方文檔:
http://seleniumhq.org/docs/05_selenium_rc.html#introduction