這個問題出現在這種情況,你的每個testCase都需要使用某一種初始化比較耗時的對象(資源),舉例如數據庫連接、Spring Context。我們遇到的問題是Selenium測試中開啟和關閉瀏覽器,如果一個test啟動關閉(我們的程序還需要登錄和注銷),這樣測試的時間會拖的很長,給持續集成帶來了困難。
所以,我們需要在每組不會沖突的test中間共享一個瀏覽器窗口,這樣也就需要一個全局的setUp和tearDown。問題是JUnit 3.8.1里面的setUp和tearDown是在每個test之前和之后運行的,如果在里面初始化和關閉瀏覽器就會造成上面所說的問題。要解決它,就產生了如下3種思路:
1、升級,使用JUnit4
JUnit4從TestNG里面吸取了兩個注釋:@BeforeClass和@AfterClass
用它們注釋過的方法就會只初始化一次,完全符合我們的需求。

public?class?SeleniumTestCase?extends?SeleneseTestCase4?
{

????protected?static?final?Log?log?=?LogFactory.getLog(SeleniumTestCase.class);
????protected?static?Selenium?selenium?=?null;


????/**?*//**
?????*?包含了登錄的代碼,保證在一個測試內部只執行一次開啟瀏覽器并登錄操作
?????*?@throws?Exception
?????*/
????@BeforeClass

????public?static?void?startSelenium()?throws?Exception?
{
????????log.debug("Starting?Selenium
");
????????selenium?=?SeleniumSession.getCurrentSession().getSelenium();
????}


????/**?*//**
?????*?在該類包含的所有測試結束之后關閉瀏覽器
?????*?@throws?Exception
?????*/
????@AfterClass

????public?static?void?stopSelenium()?throws?Exception?
{
????????log.debug("Stoping?Selenium
");
????????selenium.stop();
????}
}這個里面的selenium = SeleniumSession.getCurrentSession().getSelenium();其實是個singleton,第一次open new,后來就直接返回selenium的instance(具體參考其它文章)。
這樣做非常舒服,因為完全不是Trick,而是新的feature,用起來踏實。這樣,這個類的所有@Test就會公用一個selenium打開的瀏覽器了。
那么缺點是什么呢?缺點是放到CI環境的時候如果使用我們習慣的Ant寫執行腳本的話必須將Ant升級到1.7Beta3,因為Ant 1.6.5的Junit task不支持JUnit4……當然升級并不會帶來代碼的變化,但是問題在于Ant 1.7還是Beta,而且JUnit4需要JDK5的Annotation,你的PM估計要撇嘴了
2、JVM級別鉤子法
因為JVM支持關閉時執行制定代碼的鉤子,而static代碼會在類初始化時執行,再加上Ant調用的是類似命令行的java命令,實際上每一個測試運行在一個完整的JVM啟動關閉周期里面,所以也就產生了這種解決方案。
這個方案來自taowen同學的兩則Selenium經驗。
代碼我恢復了一下,大概是這樣:
這個方法挺酷的,我認為完全可以被稱作“奇技淫巧”。缺點就是,有點不好看。
3、還有別的方法,這個來自Selenium網站,似乎是不錯的中庸方案。
所以,我們需要在每組不會沖突的test中間共享一個瀏覽器窗口,這樣也就需要一個全局的setUp和tearDown。問題是JUnit 3.8.1里面的setUp和tearDown是在每個test之前和之后運行的,如果在里面初始化和關閉瀏覽器就會造成上面所說的問題。要解決它,就產生了如下3種思路:
1、升級,使用JUnit4
JUnit4從TestNG里面吸取了兩個注釋:@BeforeClass和@AfterClass
用它們注釋過的方法就會只初始化一次,完全符合我們的需求。



































這樣做非常舒服,因為完全不是Trick,而是新的feature,用起來踏實。這樣,這個類的所有@Test就會公用一個selenium打開的瀏覽器了。
那么缺點是什么呢?缺點是放到CI環境的時候如果使用我們習慣的Ant寫執行腳本的話必須將Ant升級到1.7Beta3,因為Ant 1.6.5的Junit task不支持JUnit4……當然升級并不會帶來代碼的變化,但是問題在于Ant 1.7還是Beta,而且JUnit4需要JDK5的Annotation,你的PM估計要撇嘴了

2、JVM級別鉤子法
因為JVM支持關閉時執行制定代碼的鉤子,而static代碼會在類初始化時執行,再加上Ant調用的是類似命令行的java命令,實際上每一個測試運行在一個完整的JVM啟動關閉周期里面,所以也就產生了這種解決方案。
這個方案來自taowen同學的兩則Selenium經驗。
代碼我恢復了一下,大概是這樣:
public?abstract?class?SomeTestCase?extends?TestCase?{
??static?{
????//?perform?the?"global"?set?up?logic
????//這里的代碼會在類初始化時執行,所以相當于BeforeClass
????log.debug("Starting?Selenium
");
????????selenium?=?SeleniumSession.getCurrentSession().getSelenium();
????//?and?now?register?the?shutdown?hook?for?tear?down?logic
????//將一個匿名方法寫到這里,就相當于AfterClass
????Runtime.getRuntime().addShutdownHook(
?????? new?Thread(){
?????????? public?void?run()?{
???????????? log.debug("Stoping?Selenium
");
???????????? selenium.stop();
?????????? }
?????? }
???? );
??}
}
??static?{
????//?perform?the?"global"?set?up?logic
????//這里的代碼會在類初始化時執行,所以相當于BeforeClass
????log.debug("Starting?Selenium

????????selenium?=?SeleniumSession.getCurrentSession().getSelenium();
????//?and?now?register?the?shutdown?hook?for?tear?down?logic
????//將一個匿名方法寫到這里,就相當于AfterClass
????Runtime.getRuntime().addShutdownHook(
?????? new?Thread(){
?????????? public?void?run()?{
???????????? log.debug("Stoping?Selenium

???????????? selenium.stop();
?????????? }
?????? }
???? );
??}
}
這個方法挺酷的,我認為完全可以被稱作“奇技淫巧”。缺點就是,有點不好看。
3、還有別的方法,這個來自Selenium網站,似乎是不錯的中庸方案。
import?junit.framework.*;
import?junit.extensions.TestSetup;
public?class?AllTestsOneTimeSetup?{
????public?static?Test?suite()?{
????????TestSuite?suite?=?new?TestSuite();
????????suite.addTest(SomeTest.suite());
????????suite.addTest(AnotherTest.suite());
????????TestSetup?wrapper?=?new?TestSetup(suite)?{
????????????protected?void?setUp()?{
????????????????oneTimeSetUp();
????????????}
????????????protected?void?tearDown()?{
????????????????oneTimeTearDown();
????????????}
????????};
????????return?wrapper;
????}
????public?static?void?oneTimeSetUp()?{
????????//?one-time?initialization?code
????}
????public?static?void?oneTimeTearDown()?{
????????//?one-time?cleanup?code
????}
}
import?junit.extensions.TestSetup;
public?class?AllTestsOneTimeSetup?{
????public?static?Test?suite()?{
????????TestSuite?suite?=?new?TestSuite();
????????suite.addTest(SomeTest.suite());
????????suite.addTest(AnotherTest.suite());
????????TestSetup?wrapper?=?new?TestSetup(suite)?{
????????????protected?void?setUp()?{
????????????????oneTimeSetUp();
????????????}
????????????protected?void?tearDown()?{
????????????????oneTimeTearDown();
????????????}
????????};
????????return?wrapper;
????}
????public?static?void?oneTimeSetUp()?{
????????//?one-time?initialization?code
????}
????public?static?void?oneTimeTearDown()?{
????????//?one-time?cleanup?code
????}
}
這個好像是比較正統的方案,不好意思我并沒有試驗,但是看起來這的確可能是限定用JDK 1.4或JUnit 3.8.1的最佳解決方案。歡迎嘗試。相關的連接參考這里:http://www.cs.wm.edu/~noonan/junit/doc/faq/faq.htm#organize_3?