1. Never use Selenium FIT mode

Selenium分為兩種運(yùn)行模式,Driven Mode(現(xiàn)在叫Selenium Remote Control)和FIT Mode(現(xiàn)在叫Selenium Core)。

FIT Mode顧名思義,就是類似FIT Testing Framework那種使用方式,主要用于QA等非技術(shù)人員編寫Web應(yīng)用的功能測(cè)試。FIT Mode的Selenium測(cè)試使用HTML來(lái)組織測(cè)試用例。例如我要測(cè)試一個(gè)web應(yīng)用的登陸功能。我可能寫出這樣的HTML 表格。

?1 < table >
?2 < tr >
?3 ? < td > open </ td >
?4 ???????? < td > http://localhost:8080/login </ td >
?5 ???????? < td ></ td >
?6 </ tr >
?7 < tr >
?8 ? < td > type </ td >
?9 ???????? < td > id=username </ td >
10 ???????? < td > someuser </ td >
11 </ tr >
12 < tr >
13 ? < td > type </ td >
14 ???????? < td > id=password </ td >
15 ???????? < td > password </ td >
16 </ tr >
17 < tr >
18 ? < td > click </ td >
19 ???????? < td > id=login_button </ td >
20 ???????? < td ></ td >
21 </ tr >
22 < tr >
23 ? < td > assertTextPresent </ td >
24 ???????? < td > Welcome?to?xxxx </ td >
25 ???????? < td ></ td >
26 </ tr >
27 </ table >

不同于FIT,Selenium內(nèi)置了一系列的命令,如上例中的open, type, click以及assertTextPresent,因此QA可以完全拋開(kāi)DEV獨(dú)立地編寫測(cè)試(FIT需要DEV提供Behavior Fixture)。因此FIT Mode是相當(dāng)容易使用的,哪怕不會(huì)使用HTML的QA,也可以使用FrontPage畫出三列表格,依次填入數(shù)據(jù)。

然而對(duì)于大多數(shù)team而言——尤其是敏捷team,F(xiàn)IT Mode平易的外表下是令人恐懼的泥沼。大多數(shù)團(tuán)隊(duì)往往選擇使用Selenium作為功能測(cè)試和集成測(cè)試工具而不僅僅是QA測(cè)試工具,在不同的迭代間遇到功能流程或UI變化時(shí),必須要重構(gòu)Selenium測(cè)試,或者說(shuō),F(xiàn)unctional Test Migration。令人遺憾的是,HTML based的Selenium FIT Testing的重構(gòu)竟然令人難以置信的困難。我們可以使用include等Selenium FIT擴(kuò)展,使得它可以重用詳細(xì)的功能(Log in, Log out諸如此類)。即便如此,在一個(gè)真實(shí)的項(xiàng)目中,Selenium Test的數(shù)量往往在200-500之間(我目前所處的項(xiàng)目在改用Driven Mode前已達(dá)350+),對(duì)于這么大基數(shù)的Selenium測(cè)試,手工重構(gòu)幾乎是不可想象的,而目前尚沒(méi)有HTML代碼重構(gòu)工具。即便存在泛泛意義上的HTML重構(gòu)工具,對(duì)于Selenium測(cè)試重構(gòu)的有效性尚待商榷。而使用Driven Mode上述代碼可以寫為:

1 public ? void ?testShouldShowAWeclomeMessageAfterUserLoggedIn()? {
2 ????selenium.open( " http://localhost:8080/login " );
3 ????selenium.type( " id=username " , " someuser " );
4 ????selenium.type( " id=password " ,? " password " );
5 ????selenium.click( " id=login_button " );
6 ????assertTrue(selenium.isTextPresent( " Welcome?to?xxxx " ));
7 }


很自然,一個(gè)訓(xùn)練有素的程序員會(huì)重構(gòu)出如下代碼:

?1 public ? void ?login(String?username,?String?password)? {
?2 ????selenium.open( " http://localhost:8080/login " );
?3 ????selenium.type( " id=username " ,username);
?4 ????selenium.type( " id=password " ,?password);
?5 ????selenium.click( " id=login_button " );?
?6 }

?7
?8 public ? void ?testShouldShowAWeclomeMessageAfterUserLoggedIn()? {
?9 ????login( " someuser " ,? " password " );
10 ????assertTrue(selenium.isTextPresent( " Welcome?to?xxxx " ));
11 }

之后無(wú)論是pull up到公共基類還是extact到Utils class都是很容易的事情。由于Java在代碼重構(gòu)上便利,Java Selenium Remote Control成為使用Selenium的最佳方式。在這一點(diǎn)上,縱使Ruby語(yǔ)法上比Java簡(jiǎn)單靈活得多,它仍不是編寫Selenium測(cè)試的最佳載體(當(dāng)然一個(gè)經(jīng)過(guò)精心設(shè)計(jì)的ruby selenium dsl wrapper還是具有非凡的價(jià)值的,這個(gè)我們后面會(huì)涉及到)。

2. Using the name user, system, page instead of selenium

觀察上面提到的代碼,其中使用selenium來(lái)操縱web應(yīng)用的行為,這在Remote Control里是常見(jiàn)的做法,但是仍然不夠好,我們可以做一些小的變化以得到更好的測(cè)試:

?1 protected ? void ?setup()? {
?2 ????selenium? = ?? // ?intialize?selenium?instance
?3 ????user? = ?selenium;
?4 ????currentPage? = ?selenium;
?5 }

?6
?7 public ? void ?login(String?username,?String?password)? {
?8 ????user.open( " http://localhost:8080/login " );
?9 ????user.type( " id=username " ,username);
10 ????user.type( " id=password " ,?password);
11 ????user.click( " id=login_button " );?
12 }

13
14 public ? void ?testShouldShowAWeclomeMessageAfterUserLoggedIn()? {
15 ????login( " some?guy " ,? " password " );
16 ????assertTrue(currentPage.isTextPresent( " Welcome?to?xxxx " ));
17 }

基本上這只不過(guò)是"另一種寫法"而已,但是它更好的表達(dá)了"用戶的行為",如login代碼所示。以及"系統(tǒng)的正確相應(yīng)",即currentPage.isTextPresent()。這種是典型的對(duì)編譯器無(wú)意義對(duì)人有意義的代碼,也就是普遍意義上好的代碼。

3. Creating a DSL base on your test codes

懂得HTML的QA可以在沒(méi)有DEV的幫助下使用Selenium FIT mode,然而卻不能在沒(méi)有DEV的幫助下使用Driven Mode。于是最自然也是最fashion的做法,就是在已有的test codes之上提供Testing DSL或者Scripting Language,讓FIT mode變得更加FIT。這方面內(nèi)容是一個(gè)更大的主題,以后再詳細(xì)展開(kāi)吧。

4. Hacking Selenium Object to support FIT command

Selenium FIT mode和RC mode下的命令有些許差異,比如FIT中的assertTextPresent,在RC中變成了isTextPresent。同樣還有FIT中最實(shí)用的命令clickAndWait,在RC中變成了click和waitForPageToLoad。在RC中使用FIT mode中的命令也非難事,找到com.thoughtworks.selenium.Selenium,添加方法:

public ? void ?doCommand(String?commmand,?String?parameters);

然后在com.thoughtworks.selenium.DefaultSelenium中添加實(shí)現(xiàn):

1 public ? void ?doCommand(String?commmand,?String?parameters)? {
2 ???String[]?paras? = ? new ?String[] { "" , "" , "" }
3 ??? for ?( int ?i? = ? 0 ;?i? < ?parameters.length? && ?i? < ? 3 ;?i ++ )
4 ??????paras[i]? = ?parameters[i];
5 ???commandProcessor.doCommand(command,?paras);
6 }

然后試驗(yàn)一下:

selenium.doCommand( " clickAndWait " );

在我們使用純RC mode之前曾經(jīng)用過(guò)一段中間方案,將rc code轉(zhuǎn)化為fit code來(lái)跑(因?yàn)閞c不支持https),由于不是真正的rc mode,像isTextPresent之類的方法都沒(méi)有辦法使用,只能使用FIT mode command。因此如果因?yàn)橐恍┨厥獾脑?https, chrome起不來(lái),hta bug多等等),你沒(méi)有辦法使用RC mode,但是有希望得到RC可重構(gòu)的好處,那么這個(gè)tricky的技巧倒是不錯(cuò)的選擇。

5. Using chrome and IE hta lanucher to support https
6. Run test using different browser lanucher to test browser compatibility

這兩個(gè)都是和browser lanucher相關(guān)的,Selenium和JWebUnit最大的不同在于它使用真實(shí)的瀏覽器來(lái)跑測(cè)試,從而可以更加真實(shí)地考察系統(tǒng)在不同瀏覽器中的表現(xiàn)。因此使用不同的瀏覽器lanucher來(lái)運(yùn)行測(cè)試,可以更好測(cè)試應(yīng)用的瀏覽器兼容性,這對(duì)于web 2.0應(yīng)用而言是很有幫助的。此外,使用rc提供的試驗(yàn)性lanucher,chrome和hta可以解決跨domain測(cè)試和https的問(wèn)題。不過(guò)目前hta還是有很多bug的,推薦使用chrome。當(dāng)然,最希望的還是澳洲的同事可以早日在selenium里提供https支持。