您好,登錄后才能下訂單哦!
小編給大家分享一下java單元測試JUnit框架原理的示例分析,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
具體如下:
1 簡介
JUnit是一個Java語言的單元測試框架,它由 Kent Beck 和 Erich Gamma 建立,逐漸成為 xUnit 家族中最為成功的一個。 JUnit有它自己的JUnit擴展生態(tài)圈,多數(shù)Java的開發(fā)環(huán)境都已經(jīng)集成了JUnit作為單元測試的工具。在這里,一個單元可以是一個方法、類、包或者子系統(tǒng)。因此,單元測試是指對代碼中的最小可測試單元進行檢查和驗證,以便確保它們正常工作。例如,我們可以給予一定的輸入測試輸出是否是所希望得到的結果。
2 特點
JUnit提供了注釋以及確定的測試方法;
JUnit提供了斷言用于測試預期的結果;
JUnit測試優(yōu)雅簡潔不需要花費太多的時間;
JUnit測試讓大家可以更快地編寫代碼并且提高質量;
JUnit測試可以組織成測試套件包含測試案例,甚至其他測試套件;
Junit顯示測試進度,如果測試是沒有問題條形是綠色的,測試失敗則會變成紅色;
JUnit測試可以自動運行,檢查自己的結果,并提供即時反饋,沒有必要通過測試結果報告來手動梳理。
3 內(nèi)容
3.1 注解
@Test :該注釋表示,用其附著的公共無效方法(即用public修飾的void類型的方法 )可以作為一個測試用例;
@Before :該注釋表示,用其附著的方法必須在類中的每個測試之前執(zhí)行,以便執(zhí)行測試某些必要的先決條件;
@BeforeClass :該注釋表示,用其附著的靜態(tài)方法必須執(zhí)行一次并在類的所有測試之前,發(fā)生這種情況時一般是測試計算共享配置方法,如連接到數(shù)據(jù)庫;
@After :該注釋表示,用其附著的方法在執(zhí)行每項測試后執(zhí)行,如執(zhí)行每一個測試后重置某些變量,刪除臨時變量等;
@AfterClass :該注釋表示,當需要執(zhí)行所有的測試在JUnit測試用例類后執(zhí)行,AfterClass注解可以使用以清理建立方法,如斷開數(shù)據(jù)庫連接,注意:附有此批注(類似于BeforeClass)的方法必須定義為靜態(tài);
@Ignore :該注釋表示,當想暫時禁用特定的測試執(zhí)行可以使用忽略注釋,每個被注解為@Ignore的方法將不被執(zhí)行。
/** * JUnit 注解示例 */ @Test public void testYeepay(){ Syetem.out.println("用@Test標示測試方法!"); } @AfterClass public static void paylus(){ Syetem.out.println("用@AfterClass標示的方法在測試用例類執(zhí)行完之后!"); }
3.2 斷言
在這里,作者將介紹一些斷言方法,所有這些方法都來自 org.junit.Assert 類,其擴展了 java.lang.Object 類并為它們提供編寫測試,以便檢測故障。簡而言之,我們就是通過斷言方法來判斷實際結果與我們預期的結果是否相同,如果相同,則測試成功,反之,則測試失敗。
void assertEquals([String message], expected value, actual value) :斷言兩個值相等,值的類型可以為int、short、long、byte、char 或者
java.lang.Object,其中第一個參數(shù)是一個可選的字符串消息;
void assertTrue([String message], boolean condition) :斷言一個條件為真;
void assertFalse([String message],boolean condition) :斷言一個條件為假;
void assertNotNull([String message], java.lang.Object object) :斷言一個對象不為空(null);
void assertNull([String message], java.lang.Object object) :斷言一個對象為空(null);
void assertSame([String message], java.lang.Object expected, java.lang.Object actual) :斷言兩個對象引用相同的對象;
void assertNotSame([String message], java.lang.Object unexpected, java.lang.Object actual) :斷言兩個對象不是引用同一個對象;
void assertArrayEquals([String message], expectedArray, resultArray) :斷言預期數(shù)組和結果數(shù)組相等,數(shù)組的類型可以為int、long、short、char、byte 或者 java.lang.Object
4 JUnit 3.X 和 JUnit 4.X 的區(qū)別
4.1 JUnit 3.X
(1)使用 JUnit 3.X 版本進行單元測試時,測試類必須要繼承于 TestCase 父類;
(2)測試方法需要遵循的原則:
① public的;
② void的;
③ 無方法參數(shù);
④方法名稱必須以 test 開頭;
(3)不同的測試用例之間一定要保持完全的獨立性,不能有任何的關聯(lián);
(4)要掌握好測試方法的順序,不能依賴于測試方法自己的執(zhí)行順序。
/** * 用 JUnit 3.X 進行測試 */ import junit.framework.Assert; import junit.framework.TestCase; public class TestOperation extends TestCase { private Operation operation; public TestOperation(String name) { // 構造函數(shù) super(name); } @Override public void setUp() throws Exception { // 在每個測試方法執(zhí)行 [之前] 都會被調(diào)用,多用于初始化 System.out.println("歡迎使用Junit進行單元測試..."); operation = new Operation(); } @Override public void tearDown() throws Exception { // 在每個測試方法執(zhí)行 [之后] 都會被調(diào)用,多用于釋放資源 System.out.println("Junit單元測試結束..."); } public void testDivideByZero() { Throwable te = null; try { operation.divide(6, 0); Assert.fail("測試失敗"); //斷言失敗 } catch (Exception e) { e.printStackTrace(); te = e; } Assert.assertEquals(Exception.class, te.getClass()); Assert.assertEquals("除數(shù)不能為 0 ", te.getMessage()); } }
4.2 JUnit 4.X
(1)使用 JUnit 4.X 版本進行單元測試時,不用測試類繼承TestCase父類;
(2)JUnit 4.X 版本,引用了注解的方式進行單元測試;
(3)JUnit 4.X 版本我們常用的注解包括:
@Before 注解:與JUnit 3.X 中的 setUp() 方法功能一樣,在每個測試方法之前執(zhí)行,多用于初始化;
@After 注解:與 JUnit 3.X 中的 tearDown() 方法功能一樣,在每個測試方法之后執(zhí)行,多用于釋放資源;
@Test(timeout = xxx) 注解:設置當前測試方法在一定時間內(nèi)運行完,否則返回錯誤;
@Test(expected = Exception.class) 注解:設置被測試的方法是否有異常拋出。拋出異常類型為:Exception.class;
此外,我們可以通過閱讀上面的第二部分“2 注解”了解更多的注解。
/** * 用 JUnit 4.X 進行測試 */ import static org.junit.Assert.*; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class TestOperation { private Operation operation; @BeforeClass public static void globalInit() { // 在所有方法執(zhí)行之前執(zhí)行 System.out.println("@BeforeClass標注的方法,在所有方法執(zhí)行之前執(zhí)行..."); } @AfterClass public static void globalDestory() { // 在所有方法執(zhí)行之后執(zhí)行 System.out.println("@AfterClass標注的方法,在所有方法執(zhí)行之后執(zhí)行..."); } @Before public void setUp() { // 在每個測試方法之前執(zhí)行 System.out.println("@Before標注的方法,在每個測試方法之前執(zhí)行..."); operation = new Operation(); } @After public void tearDown() { // 在每個測試方法之后執(zhí)行 System.out.println("@After標注的方法,在每個測試方法之后執(zhí)行..."); } @Test(timeout=600) public void testAdd() { // 設置限定測試方法的運行時間 如果超出則返回錯誤 System.out.println("測試 add 方法..."); int result = operation.add(2, 3); assertEquals(5, result); } @Test public void testSubtract() { System.out.println("測試 subtract 方法..."); int result = operation.subtract(1, 2); assertEquals(-1, result); } @Test public void testMultiply() { System.out.println("測試 multiply 方法..."); int result = operation.multiply(2, 3); assertEquals(6, result); } @Test public void testDivide() { System.out.println("測試 divide 方法..."); int result = 0; try { result = operation.divide(6, 2); } catch (Exception e) { fail(); } assertEquals(3, result); } @Test(expected = Exception.class) public void testDivideAgain() throws Exception { System.out.println("測試 divide 方法,除數(shù)為 0 的情況..."); operation.divide(6, 0); fail("test Error"); } public static void main(String[] args) { } }
4.3 特別提醒
通過以上兩個例子,我們已經(jīng)可以大致知道 JUnit 3.X 和 JUnit 4.X 兩個版本的區(qū)別啦!首先,如果我們使用 JUnit 3.X,那么在我們寫的測試類的時候,一定要繼承 TestCase 類,但是如果我們使用 JUnit 4.X,則不需繼承 TestCase 類,直接使用注解就可以啦!在 JUnit 3.X 中,還強制要求測試方法的命名為“ testXxxx ”這種格式;在 JUnit 4.X 中,則不要求測試方法的命名格式,但作者還是建議測試方法統(tǒng)一命名為“ testXxxx ”這種格式,簡潔明了。
此外,在上面的兩個示例中,我們只給出了測試類,但是在這之前,還應該有一個被測試類,也就是我們真正要實現(xiàn)功能的類?,F(xiàn)在,作者將給出上面示例中被測試的類,即 Operation 類:
/** * 定義了加減乘除的法則 */ public class Operation { public static void main(String[] args) { System.out.println("a + b = " + add(1,2)); System.out.println("a - b = " + subtract(1,2)); System.out.println("a * b = " + multiply(1,2)); System.out.println("a / b = " + divide(4,2)); System.out.println("a / b = " + divide(1,0)); } public static int add(int a, int b) { return a + b; } public static int subtract(int a, int b) { return a - b; } public static int multiply(int a, int b) { return a * b; } public static int divide(int a, int b) { return a / b; } }
5 測試示例
5.1 示例一:簡單的 JUnit 3.X 測試
import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import java.util.ArrayList; import java.util.Collection; /** * 1、創(chuàng)建一個測試類,繼承TestCase類 */ public class SimpleTestDemo extends TestCase { public SimpleTestDemo(String name) { super(name); } /** * 2、寫一個測試方法,斷言期望的結果 */ public void testEmptyCollection(){ Collection collection = new ArrayList(); assertTrue(collection.isEmpty()); } /** * 3、寫一個suite()方法,它會使用反射動態(tài)的創(chuàng)建一個包含所有的testXxxx方法的測試套件 */ public static Test suit(){ return new TestSuite(SimpleTestDemo.class); } /** * 4、寫一個main()方法,以文本運行器的方式方便的運行測試 */ public static void main(String[] args) { junit.textui.TestRunner.run(suit()); } }
5.2 示例二:套件測試
首先,介紹一下套件測試,簡單來講,測試套件是指:一些測試不同類的用例,可以使用 @RunWith 和 @Suite 注解把所有的測試類套在一起,從而形成測試套件。如果有很多測試類,想讓它們都運行在同一時間,而不是單一地運行每個測試,套件測試是非常有用的。當一個類被注解為 @RunWith, JUnit 將調(diào)用其中的注解,以便運行測試類,而不使用內(nèi)置的 JUnit 運行方法。
/** * 待測試類 */ import java.util.Arrays; public class GotoWork { public String[] prepareSkills() { String[] skill = { "Java", "MySQL", "JSP" }; System.out.println("My skills include : " + Arrays.toString(skill)); return skill; } public String[] addSkills() { String[] skill = { "Java", "MySQL", "JSP", "JUnit" }; System.out.println("Look, my skills include : " + Arrays.toString(skill)); return skill; } }
/** * 測試類 1 */ import org.junit.Test; import static org.junit.Assert.*; public class PrepareSkillsTest { GotoWork gotoWork = new GotoWork(); String[] skill = { "Java", "MySQL", "JSP" }; @Test public void testPrepareSkills() { System.out.println("Inside testPrepareSkills()"); assertArrayEquals(skill, gotoWork.prepareSkills()); } }
/** * 測試類 2 */ import org.junit.Test; import static org.junit.Assert.*; public class AddSkillsTest { GotoWork gotoWork = new GotoWork(); String[] skill = { "Java", "MySQL", "JSP", "JUnit" }; @Test public void testAddSkills() { System.out.println("Inside testAddPencils()"); assertArrayEquals(skill, gotoWork.addSkills()); } }
/** * 套件測試 */ import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ PrepareSkillsTest.class, AddSkillsTest.class }) public class SuitTest { }
使用 @Suite.SuiteClasses 注解,可以定義測試類,將被列入執(zhí)行,并且執(zhí)行的順序就是在 @Suite.SuiteClasses 注解中定義的順序。
5.3 示例三:參數(shù)化測試
首先介紹一下參數(shù)化測試,一個測試類可以被看作是一個參數(shù)化測試類,當其滿足下列所有要求:
① 該類被注解為 @RunWith(Parameterized.class);
② 該類有一個構造函數(shù),存儲測試數(shù)據(jù);
③ 該類有一個靜態(tài)方法生成并返回測試數(shù)據(jù),并標注 @Parameters 注解;
④ 該類有一個測試方法,即用注解 @Test 標注的方法。
/** * 待測試類 */ public class Calculate { public int sum(int var1, int var2) { System.out.println("此方法的參數(shù)值分別為 : " + var1 + " + " + var2); return var1 + var2; } }
/** * 參數(shù)化測試類 */ import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; 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 CalculateTest { private int expected; private int first; private int second; public CalculateTest(int expectedResult, int firstNumber, int secondNumber) { this.expected = expectedResult; this.first = firstNumber; this.second = secondNumber; } @Parameters public static Collection addedNumbers() { return Arrays.asList(new Integer[][] { { 3, 1, 2 }, { 5, 2, 3 }, { 7, 3, 4 }, { 9, 4, 5 }, }); } @Test public void testSum() { Calculate add = new Calculate(); System.out.println("Addition with parameters : " + first + " and " + second); assertEquals(expected, add.sum(first, second)); } }
觀察 CalculateTest 類,它滿足上述所有的要求,因此它就可以稱為一個參數(shù)化測試類。addedNumbers 方法使用注釋 @Parameters 返回數(shù)組的集合,每個數(shù)組包括每個測試執(zhí)行輸入和輸出數(shù)字,每個數(shù)組中的元素數(shù)必須相同好與構造參數(shù)的個數(shù)相匹配。所以,在這種特定的情況下,每個數(shù)組包括三個元素,即表示要加入的兩個元素和一個結果元素。
6 個人建議
有些童鞋可能會有一些誤解,認為寫測試代碼沒有用,而且還會增大自己的壓力,浪費時間。但事實上,寫測試代碼與否,還是有很大區(qū)別的,如果是在小的項目中,或許這種區(qū)別還不太明顯,但如果在大型項目中,一旦出現(xiàn)錯誤或異常,用人力去排查的話,那將會浪費很多時間,而且還不一定排查的出來,但是如果用測試代碼的話,JUnit 就是自動幫我們判斷一些代碼的結果正確與否,從而節(jié)省的時間將會遠遠超過你寫測試代碼的時間。
因此,個人建議:要養(yǎng)成編寫測試代碼的習慣,碼一點、測一點;再碼一點,再測一點,如此循環(huán)。在我們不斷編寫與測試代碼的過程中,我們將會對類的行為有一個更為深入的了解,從而可以有效的提高我們的工作效率。下面,作者就給出一些具體的編寫測試代碼的技巧和較好的實踐方法:
1. 不要用 TestCase 的構造函數(shù)初始化 Fixture,而要用 setUp() 和 tearDown() 方法;
2. 不要依賴或假定測試運行的順序,因為 JUnit 會利用 Vector 保存測試方法,所以不同的平臺會按不同的順序從 Vector 中取出測試方法;
3. 避免編寫有副作用的 TestCase,例如:如果隨后的測試依賴于某些特定的交易數(shù)據(jù),就不要提交交易數(shù)據(jù),只需要簡單的回滾就可以了;
4. 當繼承一個測試類時,記得調(diào)用父類的 setUp() 和 tearDown() 方法;
5. 將測試代碼和工作代碼放在一起,同步編譯和更新;
6. 測試類和測試方法應該有一致的命名方案,如在工作類名前加上 test 從而形成測試類名;
7. 確保測試與時間無關,不要使用過期的數(shù)據(jù)進行測試,以至于導致在隨后的維護過程中很難重現(xiàn)測試;
8. 如果編寫的軟件面向國際市場,那么編寫測試時一定要考慮國際化的因素;
9. 盡可能地利用 JUnit 提供地 assert 和 fail 方法以及異常處理的方法,其可以使代碼更為簡潔;
10. 測試要盡可能地小,執(zhí)行速度快;
11. 不要硬性規(guī)定數(shù)據(jù)文件的路徑;
12. 使用文檔生成器做測試文檔。
看完了這篇文章,相信你對“java單元測試JUnit框架原理的示例分析”有了一定的了解,如果想了解更多相關知識,歡迎關注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。