溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java單元測試JUnit框架原理的示例分析

發(fā)布時間:2021-08-04 09:48:52 來源:億速云 閱讀:122 作者:小新 欄目:編程語言

小編給大家分享一下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è)資訊頻道,感謝各位的閱讀!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI