在JUnit中多个testCase只执行一次setup和tearDown的方法

每个testCase都需要使用某一种初始化比较耗时的对象(资源),举例如数据库连接、Spring Context。我们遇到的问题是Selenium测试中开启和关闭浏览器,如果一个test启动关闭(我们的程序还需要登录和注销),这样测试的时间会拖的很长,给持续集成带来了困难。
所以,我们需要在每组不会冲突的test中间共享一个浏览器窗口,这样也就需要一个全局的setUp和tearDown。问题是JUnit 3.8.1里面的setUp和tearDown是在每个test之前和之后运行的,如果在里面初始化和关闭浏览器就会造成上面所说的问题。要解决它,就产生了如下3种思路:
1、升级,使用JUnit4
JUnit4从TestNG里面吸取了两个注释:@BeforeClass和@AfterClass
用它们注释过的方法就会只初始化一次,完全符合我们的需求。

Java代码  
  1. public class SeleniumTestCase extends SeleneseTestCase4 {   
  2.   
  3.     protected static final Log log = LogFactory.getLog(SeleniumTestCase.class);   
  4.     protected static Selenium selenium = null;   
  5.   
  6.     /** *//**  
  7.      * 包含了登录的代码,保证在一个测试内部只执行一次开启浏览器并登录操作  
  8.      * @throws Exception  
  9.      */  
  10.     @BeforeClass  
  11.     public static void startSelenium() throws Exception {   
  12.         log.debug("Starting Selenium");   
  13.         selenium = SeleniumSession.getCurrentSession().getSelenium();   
  14.     }   
  15.   
  16.     /** *//**  
  17.      * 在该类包含的所有测试结束之后关闭浏览器  
  18.      * @throws Exception  
  19.      */  
  20.     @AfterClass  
  21.     public static void stopSelenium() throws Exception {   
  22.         log.debug("Stoping Selenium");   
  23.         selenium.stop();   
  24.     }   
  25. }  
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启动关闭周期里面,所以也就产生了这种解决方案。

Java代码  
  1. public abstract class SomeTestCase extends TestCase {   
  2.   
  3.   static {   
  4.     // perform the "global" set up logic   
  5.     //这里的代码会在类初始化时执行,所以相当于BeforeClass   
  6.     log.debug("Starting Selenium");   
  7.         selenium = SeleniumSession.getCurrentSession().getSelenium();   
  8.   
  9.     // and now register the shutdown hook for tear down logic   
  10.     //将一个匿名方法写到这里,就相当于AfterClass   
  11.     Runtime.getRuntime().addShutdownHook(   
  12.        new Thread(){   
  13.            public void run() {   
  14.              log.debug("Stoping Selenium");   
  15.              selenium.stop();   
  16.            }   
  17.        }   
  18.      );   
  19.   }   
  20.   
  21. }  
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();
           }
       }
     );
  }

}






3、还有别的方法,这个来自Selenium网站,似乎是不错的中庸方案。

Java代码  
  1. import junit.framework.*;   
  2. import junit.extensions.TestSetup;   
  3.   
  4. public class AllTestsOneTimeSetup {   
  5.   
  6.     public static Test suite() {   
  7.   
  8.         TestSuite suite = new TestSuite();   
  9.   
  10.         suite.addTest(SomeTest.suite());   
  11.         suite.addTest(AnotherTest.suite());   
  12.   
  13.         TestSetup wrapper = new TestSetup(suite) {   
  14.   
  15.             protected void setUp() {   
  16.                 oneTimeSetUp();   
  17.             }   
  18.   
  19.             protected void tearDown() {   
  20.                 oneTimeTearDown();   
  21.             }   
  22.         };   
  23.   
  24.         return wrapper;   
  25.     }   
  26.   
  27.     public static void oneTimeSetUp() {   
  28.         // one-time initialization code   
  29.     }   
  30.   
  31.     public static void oneTimeTearDown() {   
  32.         // one-time cleanup code   
  33.     }   
  34. }  
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
    }
}





这个好像是比较正统的方案,不好意思我并没有试验,但是看起来这的确可能是限定用JDK 1.4或JUnit 3.8.1的最佳解决方案。欢迎尝试。相关的连接参考这里:http://www.cs.wm.edu/~noonan/junit/doc/faq/faq.htm#organize_3

转自 :http://www.blogjava.net/iamtin/archive/2006/11/17/81802.html

你可能感兴趣的:(在JUnit中多个testCase只执行一次setup和tearDown的方法)