一、為什麼需要單元測試
在平時的開發當中,一個項目往往包含了大量的方法,可能有成千上萬個。如何去保證這些方法產生的結果是我們想要的呢?當然了,最容易想到的一個方式,就是我們通過System.out來輸出我們的結果,看看是不是滿足我們的需求,但是項目中這些成千上萬個方法,我們總不能在每一個方法中都去輸出一遍嘛。這也太枯燥了。這時候用我們的單元測試框架junit就可以很好地解決這個問題。
junit如何解決這個問題的呢?答案在於內部提供了一個斷言機制,他能夠將我們預期的結果和實際的結果進行比對,判斷出是否滿足我們的期望。相信到這,你已經迫不及待的想認識一下junit,下面我們直接通過案例,來分析一下這個機制。
二、從案例講起
1、預備工作
junit4是一個單元測試框架,既然是框架,這也就意味著jdk並沒有為我們提供api,因此在這裡我們就需要導入相關的依賴。對於IDEA來說,你在構建Maven項目的時候會直接自動添加相關的依賴,如果沒有,手動添加即可:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
這裡的版本是4.12。當然還有最新的版本。你可以手動選擇。這裡選用的是4的版本。
2、案例
這裡我們要測試的功能超級簡單,就是加減乘除法的驗證。
public class Calculate { public int add(int a,int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a,int b) { return a * b; } public int divide(int a ,int b) { return a / b; } }
然後我們看看如何使用junit去測試。
public class CalculateTest { @Test public void testAdd() { assertEquals(2, new Calculate().add(1,1)); } @Test public void testSubtract() { assertEquals(8, new Calculate().subtract(10,2)); } @Test public void testMultiply() { assertEquals(6, new Calculate().multiply(3, 2)); } @Test public void testDivide() { assertEquals(5, new Calculate().divide(10, 2)); } }
以上就是我們的單元測試,需要遵循一下規則:
1、每一個測試方法上使用@Test進行修飾
2、每一個測試方法必須使用public void 進行修飾
3、每一個測試方法不能攜帶參數
4、測試代碼和源代碼在兩個不同的項目路徑下
5、測試類的包應該和被測試類保持一致
6、測試單元中的每個方法必須可以獨立測試
以上的6條規則,是在使用單元測試的必須項,當然junit也建議我們在每一個測試方法名加上test前綴,表明這是一個測試方法。
assertEquals是一個斷言的規則,裡面有兩個參數,第一個參數表明我們預期的值,第二個參數表示實際運行的值。不過junit5對這些做出了一些改變,我們會在後續的文章中專門介紹。
我們運行一下測試類,就會運行每一個測試方法,我們也可以運行某一個,只需要在相應的測試方法上面右鍵運行即可。如果運行成功編輯器的控制檯不會出現錯誤信息,如果有就會出現failure等信息。
3、運行流程
在上面的每一個測試方法中,代碼是相當簡單的,就一句話。現在我們分析一下這個測試的流程是什麼:
public class JunitFlowTest { @BeforeClass public static void setUpBeforeClass() throws Exception { System.out.println("beforeClass..."); } @AfterClass public static void tearDownAfterClass() throws Exception { System.out.println("afterClass..."); } @Before public void setUp() throws Exception { System.out.println("before..."); } @After public void tearDown() throws Exception { System.out.println("after"); } @Test public void test1() { System.out.println("test1方法..."); } @Test public void test2(){ System.out.println("test2方法..."); } }
在上面的代碼中,我們使用了兩個測試方法,還有junit運行整個流程方法。我們可以運行一下,就會出現下面的運行結果:
beforeClass... before... test1方法... after before... test2方法... after afterClass...
從上面的結果我們來畫一張流程圖就知道了:
這個流程相信應該能看懂,如果我們使用過SSM等其他的一些框架,經常會在before中添加打開數據庫等預處理的代碼,也會在after中添加關閉流等相關代碼。
以上這個案例如果能看懂,基本上算是入門了。其實這個案例也比較簡單。相信以大家聰明的頭腦能看懂。下面我們看看junit中的註解。
三、註解
對於@Test,裡面有很多參數供我們去選擇。我們來認識一下
1、@Test(expected=XX.class)
這個參數表示我們期望會出現什麼異常,比如說在除法中,我們1/0會出現ArithmeticException異常,那這裡@Test(expected=ArithmeticException.class)。在測試這個除法時候依然能夠通過。
2、@Test(timeout=毫秒 )
這個參數表示如果測試方法在指定的timeout內沒有完成,就會強制停止。
3、@Ignore
這個註解其實基本上不用,他的意思是所修飾的測試方法會被測試運行器忽略。
4、@RunWith
更改測試運行器。
四、測試套件
在文中一開始我們曾經提到,如果我們的項目中如果有成千上萬個方法,那此時也要有成千上萬個測試方法嘛?如果這樣junit使用起來還不如System.out呢,現在我們認識一下測試嵌套的方法,他的作用是我們把測試類封裝起來,也就是把測試類嵌套起來,只需要運行測試套件,就能運行所有的測試類了。、
//這裡有很多個測試類 public class Test1 { @Test public void test() { System.out.println("測試類1"); } } public class Test2 { @Test public void test() { System.out.println("測試類2"); } } //這裡一次可以類推
下面我們使用測試套件,把這些測試類嵌套在一起。
@RunWith(Suite.class) @Suite.SuiteClasses({Test1.class,Test2.class等相關測試類}) public class SuiteTest { /* * 寫一個空類:不包含任何方法 * 更改測試運行器Suite.class * 將測試類作為數組傳入到Suite.SuiteClasses({})中 */ }
也很簡單,下面我們看一下,參數化設置。
五、參數化設置
什麼是參數化設置呢?在一開始的代碼中我們看到,測試加法的時候是1+1,不過我們如果要測試多組數據怎麼辦?總不能一個一個輸入,然後運行測試吧。這時候我們可以把我們需要測試的數據先配置好。
@RunWith(Parameterized.class) public class ParameterTest { int expected =0; int input1 = 0; int input2 = 0; @Parameters public static Collection<Object[]> t() { return Arrays.asList(new Object[][]{ {3,1,2}, {4,2,2} }) ; } public ParameterTest(int expected,int input1,int input2) { this.expected = expected; this.input1 = input1; this.input2 = input2; } @Test public void testAdd() { assertEquals(expected, new Calculate().add(input1, input2)); } }
這時候再去測試,只需要去選擇相應的值即可,避免了我們一個一個手動輸入。
對於junit測試,常用的使用方法就是這麼多,關於深入瞭解,只能放在後面的課程中了。今天先到這。