1. 程式人生 > >Junit使用教程(三) .

Junit使用教程(三) .

四、例項總結

1. 引數化測試

有時一個測試方法,不同的引數值會產生不同的結果,那麼我們為了測試全面,會把多個引數值都寫出來並一一斷言測試,這樣有時難免費時費力,這是我們便可以採用引數化測試來解決這個問題。引數化測試就好比把一個“輸入值,期望值”的集合傳入給測試方法,達到一次性測試的目的。

[java] view plaincopyprint?
  1. package test;  
  2. importstatic org.junit.Assert.*;  
  3. import java.util.Arrays;  
  4. import org.junit.Test;  
  5. import org.junit.runner.RunWith;  
  6. import org.junit.runners.Parameterized;  
  7. import org.junit.runners.Parameterized.Parameters;  
  8. @RunWith(Parameterized.class)  
  9. publicclass FibonacciTest {  
  10. @Parameters(name = "{index}: fib({0})={1}")  
  11. publicstatic Iterable<Object[]> data() {  
  12. return Arrays.asList(new Object[][] { { 00 }, { 11 }, { 
    21 },  
  13.                 { 32 }, { 43 }, { 55 }, { 68 } });  
  14.     }  
  15. privateint input;  
  16. privateint expected;  
  17. public FibonacciTest(int input, int expected) {  
  18. this.input = input;  
  19. this.expected = expected;  
  20.     }  
  21. @Test
  22. publicvoid test() {  
  23.         assertEquals(expected, Fibonacci.compute(input));  
  24.     }  
  25. }  
  26. class Fibonacci {  
  27. publicstaticint compute(int input) {  
  28. int result;  
  29. switch (input) {  
  30. case0:  
  31.             result = 0;  
  32. break;  
  33. case1:  
  34. case2:  
  35.             result = 1;  
  36. break;  
  37. case3:  
  38.             result = 2;  
  39. break;  
  40. case4:  
  41.             result = 3;  
  42. break;  
  43. case5:  
  44.             result = 5;  
  45. break;  
  46. case6:  
  47.             result = 8;  
  48. break;  
  49. default:  
  50.             result = 0;  
  51.         }  
  52. return result;  
  53.     }  
  54. }  
package test;

import static org.junit.Assert.*;

import java.util.Arrays;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class FibonacciTest {

    @Parameters(name = "{index}: fib({0})={1}")
    public static Iterable<Object[]> data() {
        return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
                { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
    }

    private int input;
    private int expected;

    public FibonacciTest(int input, int expected) {
        this.input = input;
        this.expected = expected;
    }

    @Test
    public void test() {
        assertEquals(expected, Fibonacci.compute(input));
    }
}

class Fibonacci {

	public static int compute(int input) {
		int result;
		switch (input) {
		case 0:
			result = 0;
			break;
		case 1:
		case 2:
			result = 1;
			break;
		case 3:
			result = 2;
			break;
		case 4:
			result = 3;
			break;
		case 5:
			result = 5;
			break;
		case 6:
			result = 8;
			break;
		default:
			result = 0;
		}
		return result;
	}
}

@Parameters註解引數name,實際是測試方法名稱。由於一個test()方法就完成了所有測試,那假如某一組測試資料有問題,那在Junit的結果頁面裡該如何呈現?因此採用name實際上就是區分每個測試資料的測試方法名。如下圖:

2. 打包測試

同樣,如果一個專案中有很多個測試用例,如果一個個測試也很麻煩,因此打包測試就是一次性測試完成包中含有的所有測試用例。

[java] view plaincopyprint?
  1. package test;  
  2. import org.junit.runner.RunWith;  
  3. import org.junit.runners.Suite;  
  4. @RunWith(Suite.class)  
  5. @Suite.SuiteClasses({ AssertTests.class, FibonacciTest.class, JDemoTest.class })  
  6. publicclass AllCaseTest {  
  7. }  
package test;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({ AssertTests.class, FibonacciTest.class, JDemoTest.class })
public class AllCaseTest {

}

這個功能也需要使用一個特殊的Runner ,需要向@RunWith註解傳遞一個引數Suite.class 。同時,我們還需要另外一個註解@Suite.SuiteClasses,來表明這個類是一個打包測試類。並將需要打包的類作為引數傳遞給該註解就可以了。至於AllCaseTest隨便起一個類名,內容為空既可。執行AllCaseTest類即可完成打包測試

3. 異常測試

異常測試與普通斷言測試不同,共有三種方法,其中最為靈活的是第三種,可以與斷言結合使用

第一種:

[java] view plaincopyprint?
  1. @Test(expected= IndexOutOfBoundsException.class)   
  2. publicvoid empty() {   
  3. new ArrayList<Object>().get(0);   
  4. }  
  @Test(expected= IndexOutOfBoundsException.class) 
  public void empty() { 
       new ArrayList<Object>().get(0); 
  }

第二種:

[java] view plaincopyprint?
  1. @Test
  2. publicvoid testExceptionMessage() {  
  3. try {  
  4. new ArrayList<Object>().get(0);  
  5.         fail("Expected an IndexOutOfBoundsException to be thrown");  
  6.     } catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {  
  7.         assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));  
  8.     }  
  9. }  
  @Test
  public void testExceptionMessage() {
      try {
          new ArrayList<Object>().get(0);
          fail("Expected an IndexOutOfBoundsException to be thrown");
      } catch (IndexOutOfBoundsException anIndexOutOfBoundsException) {
          assertThat(anIndexOutOfBoundsException.getMessage(), is("Index: 0, Size: 0"));
      }
  }

第三種:

[java] view plaincopyprint?
  1. @Rule
  2. public ExpectedException thrown = ExpectedException.none();  
  3. @Test
  4. publicvoid shouldTestExceptionMessage() throws IndexOutOfBoundsException {  
  5.     List<Object> list = new ArrayList<Object>();  
  6.     thrown.expect(IndexOutOfBoundsException.class);  
  7.     thrown.expectMessage("Index: 0, Size: 0");  
  8.     list.get(0);  
  9.     Assert.assertEquals(1, list.get(0));  
  10. }  
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void shouldTestExceptionMessage() throws IndexOutOfBoundsException {
        List<Object> list = new ArrayList<Object>();

        thrown.expect(IndexOutOfBoundsException.class);
        thrown.expectMessage("Index: 0, Size: 0");
        list.get(0);
        Assert.assertEquals(1, list.get(0));
    }

在上述幾種方法中,無論是expected還是expect都表示期望丟擲的異常,假如某一方法,當引數為某一值時會丟擲異常,那麼使用第一種方法就必須為該引數單獨寫一個測試方法來測試異常,而無法與其他引數值一同寫在一個測試方法裡,所以顯得累贅。第二種方法雖然解決這個問題,但是寫法不僅繁瑣也不利於理解。而第三種犯法,不僅能動態更改期望丟擲的異常,與斷言語句結合的也非常好,因此推薦使用該方法來測試異常。

4. 限時測試

有時為了防止出現死迴圈或者方法執行過長(或檢查方法效率),而需要使用到限時測試。顧名思義,就是超出設定時間即視為測試失敗。共有兩種寫法。

第一種:

[java] view plaincopyprint?
  1. @Test(timeout=1000)  
  2. publicvoid testWithTimeout() {  
  3.   ...  
  4. }  
@Test(timeout=1000)
public void testWithTimeout() {
  ...
}

第二種:

[java] view plaincopyprint?
  1. publicclass HasGlobalTimeout {  
  2. publicstatic String log;  
  3. @Rule
  4. public Timeout globalTimeout = new Timeout(10000); // 10 seconds max per method tested
  5. @Test
  6. publicvoid testInfiniteLoop1() {  
  7.         log += "ran1";  
  8. for (;;) {  
  9.         }  
  10.     }  
  11. @Test
  12. publicvoid testInfiniteLoop2() {  
  13.         log += "ran2";  
  14. for (;;) {  
  15.         }  
  16.     }  
  17. }  
public class HasGlobalTimeout {
    public static String log;

    @Rule
    public Timeout globalTimeout = new Timeout(10000); // 10 seconds max per method tested

    @Test
    public void testInfiniteLoop1() {
        log += "ran1";
        for (;;) {
        }
    }

    @Test
    public void testInfiniteLoop2() {
        log += "ran2";
        for (;;) {
        }
    }
}

其中,第二種方法與異常測試的第三種方法的寫法類似。也是推薦的寫法。

至此,Junit的教程總結性文章已介紹完了。通過系統總結也進一步加深了對Junit的認識,希望也能同樣幫助到對Junit還不太理解的朋友。如果大家還有什麼好的建議和用法,很歡迎能提出來一起交流。