最開始我們需要驗(yàn)證在用戶名和密碼都正確的情況下,能夠正常登錄系統(tǒng),我們這樣編寫測(cè)試代碼(以下都是偽代碼,使用TestNG和Selenium):
@Test
def should_login_success_with_exist_username_and_correct_password(){
LoginPage page = user.open(LoginPage,"/login.html")
user.perform("login",['user1','1234'],on(page))
assert page.successLogin
}
def should_login_success_with_exist_username_and_correct_password(){
LoginPage page = user.open(LoginPage,"/login.html")
user.perform("login",['user1','1234'],on(page))
assert page.successLogin
}
恩,很不錯(cuò),運(yùn)行一下,出現(xiàn)紅條。為什么呢?原來測(cè)試數(shù)據(jù)庫(kù)里沒有用戶名為user1的用戶,好吧,寫個(gè)數(shù)據(jù)庫(kù)數(shù)據(jù)初始化腳本。再運(yùn)行,OK,綠條!
那么,接下來我們?cè)僭黾右粋€(gè)測(cè)試,需要覆蓋密碼錯(cuò)誤時(shí)不能登錄系統(tǒng)的情況,很快測(cè)試就完成了:
@Test
def should_login_success_with_exist_username_and_incorrect_password(){
LoginPage page = user.open(LoginPage,"/login.html")
user.perform("login",['user1','4321'],on(page))
assert page.successLogin,false
}
再運(yùn)行一下測(cè)試,綠條。好啦,現(xiàn)在可以看下這段代碼,恩,有些重復(fù),重構(gòu)一下:
@Test
def should_login_success_with_exist_username_and_correct_password(){
assert login('user1','1234')
}
@Test
def should_login_success_with_exist_username_and_incorrect_password(){
assert login('user1','4321'),false
}
def login(username,password){
LoginPage page = user.open(LoginPage,"/login.html")
user.perform("login",[username,password],on(page))
return page.successLogin
}
def should_login_success_with_exist_username_and_correct_password(){
assert login('user1','1234')
}
@Test
def should_login_success_with_exist_username_and_incorrect_password(){
assert login('user1','4321'),false
}
def login(username,password){
LoginPage page = user.open(LoginPage,"/login.html")
user.perform("login",[username,password],on(page))
return page.successLogin
}
重構(gòu)完成,可以看到,我們的測(cè)試方法里現(xiàn)在沒有了任何行為,僅僅是數(shù)據(jù)!這樣讓我感覺有點(diǎn)怪,不管了,先用TestNG提供的@dataProvider整理一下:
@Test(dataProvider="testdata")
def testLogin(username,password,expected){
LoginPage page = user.open(LoginPage,"/login.html")
user.perform("login",[username,password],on(page))
assert page.successLogin,expected
}
@DataProvider(name="testdata")
def Object[][] dataForLogin(){
def data=new Object[2][]
data[0]=['user1','1234',true] as Object[]
data[1]=['user1','4321',false] as Object[]
}
def testLogin(username,password,expected){
LoginPage page = user.open(LoginPage,"/login.html")
user.perform("login",[username,password],on(page))
assert page.successLogin,expected
}
@DataProvider(name="testdata")
def Object[][] dataForLogin(){
def data=new Object[2][]
data[0]=['user1','1234',true] as Object[]
data[1]=['user1','4321',false] as Object[]
}
測(cè)試方法只剩下了一個(gè)!如果要測(cè)試不存在的用戶不能登錄系統(tǒng)呢?很簡(jiǎn)單,增加數(shù)據(jù)即可!
@DataProvider(name="testdata")
def Object[][] dataForLogin(){
def data=new Object[2][]
data[0]=['user1','1234',true] as Object[]
data[1]=['user1','4321',false] as Object[]
data[1]=['inexistuser','1234',false] as Object[]
}
def Object[][] dataForLogin(){
def data=new Object[2][]
data[0]=['user1','1234',true] as Object[]
data[1]=['user1','4321',false] as Object[]
data[1]=['inexistuser','1234',false] as Object[]
}
在我們的測(cè)試方法里,測(cè)試數(shù)據(jù)和測(cè)試的行為進(jìn)行了完全的分離。從系統(tǒng)的功能來說,功能一旦實(shí)現(xiàn),那么就是一個(gè)黑盒,我們只要提供數(shù)據(jù)即可進(jìn)行測(cè)試,這個(gè)數(shù)據(jù)包括兩部分:輸入和期待的輸出。我開始暗自嘀咕:難道我以前那么多的洋洋得意測(cè)試方法很多都是不需要的嗎?這些方式為什么會(huì)存在呢?恩,想起來了,這些方法是為了覆蓋功能的各個(gè)路徑的,是提高測(cè)試覆蓋率的。那么為什么會(huì)產(chǎn)生這么多的測(cè)試方法呢?哦,在這些測(cè)試方法里,測(cè)試數(shù)據(jù)和測(cè)試行為是耦合在一起的!
我伸了個(gè)懶腰,突然想,這下好了,我已經(jīng)封裝好了功能行為,QA想增加測(cè)試用例只需要自己增加數(shù)據(jù)就可以了,嘿嘿,爽啊。說曹操,QA到。QA mm說,你的某個(gè)功能實(shí)現(xiàn)有問題。我瞅了一眼,說,不可能啊(這是dev面對(duì)bug的第一反應(yīng)),俺有測(cè)試的,持續(xù)集成一直是綠條的。QA mm說,在你的開發(fā)環(huán)境下測(cè)試是沒有問題的,但是在QA環(huán)境,因?yàn)閿?shù)據(jù)庫(kù)變了,數(shù)據(jù)變了,應(yīng)用服務(wù)器變了,所以會(huì)有些問題。我極不情愿的登錄到QA環(huán)境,一測(cè)試,還真是,郁悶。
怎么辦?修復(fù)完BUG,第一反應(yīng)就是自動(dòng)化測(cè)試能不能跑在QA環(huán)境呢?一般情況下,這些測(cè)試需要干凈的測(cè)試環(huán)境,我們會(huì)制造很多的測(cè)試數(shù)據(jù),可是在QA環(huán)境下,QA有她自己的測(cè)試數(shù)據(jù),這些數(shù)據(jù)都不存在了哈。恩,看看剛才的測(cè)試代碼,哈,就用QA的數(shù)據(jù)也可以啊,心情愉悅的改下:
@DataProvider(name="testdata")
def Object[][] dataForLogin(){
def data=new Object[2][]
data[0]=['hrong','1234',true] as Object[]
data[1]=['hrong','4321',false] as Object[]
data[1]=['rhao','1234',false] as Object[]
}
def Object[][] dataForLogin(){
def data=new Object[2][]
data[0]=['hrong','1234',true] as Object[]
data[1]=['hrong','4321',false] as Object[]
data[1]=['rhao','1234',false] as Object[]
}
OK,完成!為了該測(cè)試既能在開發(fā)測(cè)試環(huán)境運(yùn)行又能在QA環(huán)境下運(yùn)行,我們可以引入一個(gè)環(huán)境變量,將測(cè)試數(shù)據(jù)扔到文件里,通過環(huán)境變量來加載不同的測(cè)試數(shù)據(jù)(測(cè)試文件)。
好吧,喝點(diǎn)東西(甲流很厲害,喝板藍(lán)根好了),總結(jié)一下:
數(shù)據(jù)驅(qū)動(dòng)測(cè)試:測(cè)試數(shù)據(jù)與測(cè)試行為分離,通過數(shù)據(jù)來驅(qū)動(dòng)測(cè)試。
好處:在對(duì)測(cè)試行為封裝好的情況下,QA mm能夠自己通過數(shù)據(jù)修改自動(dòng)化測(cè)試;
自動(dòng)化測(cè)試能夠運(yùn)行在多個(gè)環(huán)境下(開發(fā)環(huán)境、QA環(huán)境、產(chǎn)品環(huán)境)
測(cè)試的可讀性
測(cè)試方法大量壓縮
適用范圍:功能測(cè)試(selenium測(cè)試)
通過環(huán)境準(zhǔn)備測(cè)試數(shù)據(jù)(非測(cè)試用例自己準(zhǔn)備數(shù)據(jù))
可能存在的問題:比一般的測(cè)試編寫困難,特別是在靜態(tài)語言里
最后:該文章的思考來自于徐昊在團(tuán)隊(duì)內(nèi)部的相應(yīng)Session.
http://www.aygfsteel.com/ronghao 榮浩原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處:)