1. 程式人生 > >用Rules來控制JUnit的執行

用Rules來控制JUnit的執行

本文主要依據以下幾篇介紹及個人理解所整理,轉載請註明,發現錯誤請指出,謝謝。

JUnit Rule目的

JUnit Rules是最新加入到JUnit庫中的,是setUp() tearDown()方法及最新的@Before、@After標記的演化產品。它能夠實現@Before、@After標記所能實現的所有功能,並且還有所擴充套件。總結起來,Rules主要用以以下目的:

  1. 在執行測試之前,能夠得到測試名以做一些需要的處理
  2. 指導Swing來執行測試
  3. 對測試失敗的case加入額外資訊作為參考,尤其對於selenium有作用,如抓屏

認識JUnit Rule

JUnit Rule主要包含三要素,執行測試的語句、選擇需要執行哪些語句的規則以及@Rule標記。

在加入了@Rule後JUnit按如下流程執行測試:

  1. 建立預設語句執行測試
  2. 找出測試類中所有的標記了@Rule的public的成員規則
  3. 呼叫Rule中的apply()方法並告訴該方法所要執行的測試類和測試方法以及JUnit所收集到的語句。apply()方法決定怎麼來執行測試,選擇或者建立語句來執行測試並返回相應的語句傳給JUnit。然後JUnit把語句傳給下一個rule的apply方法,如此迴圈直到最後一條規則。
  4. 呼叫最後一條Rule返回的語句的evaluate()來執行測試

如何寫JUnit Rule

JUnit比較之前的版本用的主要是MethodRule,後面改用TestRule,下面以實際例子來展現如何撰寫TestRule並加入到Test類中。

class DemoTestRule implements TestRule{
    public Statement apply(final Statement base,
                           final org.junit.runner.Description description) {
        return new Statement(){
            @Override
            public void evaluate() throws Throwable{
                try{
                    base.evaluate();
                }
                catch(Throwable t){
                    System.out.println("Test rule suggest that "+description.getMethodName()+" failed");
                    throw t;
                }
            }
        };
    }
}
上面已經定義了一樣例TestRule,那麼它如何用在Test類中呢,下面的程式碼可以很好的解釋:
public class MathTest {
	
	@Rule
	public TestRule testRule = new DemoTestRule();
	
	public MathTest(){
		System.out.println("Create MathTest instance ...");
	}

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		System.out.println("Call @BeforeClass");
	}
	
	@AfterClass
	public static void tearDownAfterClass() throws Exception {
		System.out.println("Call @AfterClass");
	}
	
	@Before
	public void setUp() throws Exception {
		System.out.println("Call @Before before a test case!");
	}

	@After
	public void tearDown() throws Exception {
		System.out.println("Call @After after a test case!");
	}

	@Test
	public void testAbs() {
		Math math = new Math();
        assertEquals(200, math.abs(200));
        assertEquals(100, math.abs(-100));
        assertEquals(0, math.abs(0));
	}

	@Test
	public void testDiv() {
		Math math = new Math();
		assertEquals(2,math.div(9, 4));
		assertEquals(5,math.div(100, 20));
	}

	@Test
	public void testExp() {
		 Math math = new Math();
		 assertEquals(32f, math.exp(2, 5), 0.001f);
	     assertEquals(1f, math.exp(2, 0), 0.001f);
	     assertEquals(0.5f, math.exp(2, (-1)), 0.001f);   
	}
	
	@Ignore("Not for the test class")
	@Test(timeout=1)
	public void testTimeLimitedExceeded() {
		double d = 0;
		for(int i = 0;i < 10000000; i ++)
			d += i;
	}
	
	@Test(expected=ArithmeticException.class)
	public void testDivByZero() {
		System.out.println("Started test divided by 0...");
		new Math().div(1, 0);
	}
	
	public static void main(String args[]){
		JUnitCore junitCore = new JUnitCore();
		junitCore.addListener(new RunListener() {
			public void testStarted(Description description) throws Exception {
				System.out.println(description.getDisplayName() + "...started");
			}
			public void testIgnored(Description description) throws Exception {
				System.out.println(description.getDisplayName() + "...failed");
			}
			});
		Result result = junitCore.runClasses(MathTest.class);
		for(Failure failure : result.getFailures())
			System.out.println(failure.getTestHeader()+":"+failure.getMessage());
	}

}

我們可以看到,只需要在Test類的開始用annotation @Rule來標記一個TestRule的宣告及初始化即完成了。在執行過程中,我們明顯能夠發現當測試失敗的時候DemoTestRule中的提示資訊就會輸出。

附Math.java檔案

public class Math {
	public int abs(int a){
		if(a >= 0)return a;
		else return -a;
	}
	public int div(int a,int b){
		return a/b;
	}
	public float exp(int a,int b){
		float r = 1;
		for(int i = 0;i < b;i ++)
			r *= a;
		return r;
	}
}

利用JUnit Rule和Selenium抓截圖

在進行網頁UI測試時候,往往我們沒有時間去跟蹤測試過程。在自動化測試過程中,什麼地方出錯了,錯誤是什麼樣子對於我們來分析是產品問題還是程式碼問題很有幫助。可是往往光靠提示的日誌資訊往往不足以讓我們得到分析結果。於是我們就可以利用selenium的captureScreenShot來抓屏,而且結合TestRule,設定規則當failure後觸發該動作。

class ScreenshotTestRule implements TestRule{
   
    protected Selenium selenium;
    protected String filePath="/home/user";
    public ScreenshtTestTule(Selenium selenium){
    	this.selenium = selenium;
    }
    public ScreenshtTestTule(Selenium selenium,String path){
    	this.selenium = selenium;
    	this.filePath = path;
    }
    public Statement apply(final Statement base,
                           final org.junit.runner.Description description) {
        return new Statement(){
            @Override
            public void evaluate() throws Throwable{
                try{
                    base.evaluate();
                }
                catch(Throwable t){
                    selenium.captureScreenshot(filePath+description.getMethodName()+".png");
                    System.out.println("Test rule suggest that "+description.getMethodName()+" failed");
                    throw t;
                }
            }
        };
    }
}