走進JavaWeb技術世界11:單元測試框架Junit

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到個人倉庫裏查看html

https://github.com/h2pl/Java-Tutorial前端

喜歡的話麻煩點下Star哈java

文章首發於個人我的博客:python

www.how2playlife.comandroid

本文是微信公衆號【Java技術江湖】的《走進JavaWeb技術世界》其中一篇,本文部份內容來源於網絡,爲了把本文主題講得清晰透徹,也整合了不少我認爲不錯的技術博客內容,引用其中了一些比較好的博客文章,若有侵權,請聯繫做者。git

該系列博文會告訴你如何從入門到進階,從servlet到框架,從ssm再到SpringBoot,一步步地學習JavaWeb基礎知識,並上手進行實戰,接着瞭解JavaWeb項目中常常要使用的技術和組件,包括日誌組件、Maven、Junit,等等內容,以便讓你更完整地瞭解整個JavaWeb技術體系,造成本身的知識框架。程序員

若是對本系列文章有什麼建議,或者是有什麼疑問的話,也能夠關注公衆號【Java技術江湖】聯繫做者,歡迎你參與本系列博文的創做和修訂。github

文末贈送8000G的Java架構師學習資料,須要的朋友能夠到文末了解領取方式,資料包括Java基礎、進階、項目和架構師等免費學習資料,更有數據庫、分佈式、微服務等熱門技術學習視頻,內容豐富,兼顧原理和實踐,另外也將贈送做者原創的Java學習指南、Java程序員面試指南等乾貨資源)面試

<!-- more -->數據庫

簡介

測試 在軟件開發中是一個很重要的方面,良好的測試能夠在很大程度決定一個應用的命運。 軟件測試中,主要有3大種類:

  • 單元測試 單元測試主要是用於測試程序模塊,確保代碼運行正確。單元測試是由開發者編寫並進行運行測試。通常使用的測試框架是 JUnit 或者 TestNG。測試用例通常是針對_方法_ 級別的測試。
  • 集成測試 集成測試用於檢測系統是否能正常工做。集成測試也是由開發者共同進行測試,與單元測試專一測試我的代碼組件不一樣的是,集成測試是系統進行跨組件測試。
  • 功能性測試 功能性測試是一種質量保證過程以及基於測試軟件組件的規範下的由輸入獲得輸出的一種黑盒測試。功能性測試一般由不一樣的測試團隊進行測試,測試用例的編寫要遵循組件規範,而後根據測試輸入獲得的實際輸出與指望值進行對比,判斷功能是否正確運行。

概述

本文只對 單元測試 進行介紹,主要介紹如何在 Android Studio 下進行單元測試,單元測試使用的測試框架爲 JUnit

好處

可能目前仍有很大一部分開發者未使用 單元測試 對他們的代碼進行測試,一方面多是以爲沒有必要,由於即便沒有進行單元測試,程序照樣運行得很好;另外一方面,也許有些人也認同單元測試的好處,可是因爲須要額外的學習成本,因此不少人也是沒有時間或者說是沒有耐心進行學習······ 這裏我想說的是,若是你們去看下 github 上目前主流的開源框架,star 數比較多的項目,通常都有很詳盡的測試用例。因此說,單元測試對於咱們的項目開發,仍是挺有好處的。 至於單元測試的好處,我這裏說起幾點:

  • 保證代碼運行與咱們預想的同樣,代碼正確性能夠獲得保證
  • 程序運行出錯時,有利於咱們對錯誤進行查找(由於咱們忽略咱們測試經過的代碼)
  • 有利於提高代碼架構設計(用於測試的用例應力求簡單低耦合,所以編寫代碼的時候,開發者每每會爲了對代碼進行測試,將其餘耦合的部分進行解耦處理) ······

Junit單元測試

本文實例講述了java單元測試JUnit框架原理與用法。分享給你們供你們參考,具體以下:

1 簡介

JUnit是一個Java語言的單元測試框架,它由 Kent Beck 和 Erich Gamma 創建,逐漸成爲 xUnit 家族中最爲成功的一個。

JUnit有它本身的JUnit擴展生態圈,多數Java的開發環境都已經集成了JUnit做爲單元測試的工具。在這裏,一個單元能夠是一個方法、類、包或者子系統。

所以,單元測試是指對代碼中的最小可測試單元進行檢查和驗證,以便確保它們正常工做。例如,咱們能夠給予必定的輸入測試輸出是不是所但願獲得的結果。在本篇博客中,做者將着重介紹 JUnit 4.X 版本的特性,這也是咱們在平常開發中使用最多的版本。

2 特色

JUnit提供了註釋以及肯定的測試方法;
JUnit提供了斷言用於測試預期的結果;
JUnit測試優雅簡潔不須要花費太多的時間;
JUnit測試讓你們能夠更快地編寫代碼而且提升質量;
JUnit測試能夠組織成測試套件包含測試案例,甚至其餘測試套件;
Junit顯示測試進度,若是測試是沒有問題條形是綠色的,測試失敗則會變成紅色;
JUnit測試能夠自動運行,檢查本身的結果,並提供即時反饋,沒有必要經過測試結果報告來手動梳理。

3 內容

3.1 註解

@Test :該註釋表示,用其附着的公共無效方法(即用public修飾的void類型的方法 )能夠做爲一個測試用例;

@Before :該註釋表示,用其附着的方法必須在類中的每一個測試以前執行,以便執行測試某些必要的先決條件;

@BeforeClass :該註釋表示,用其附着的靜態方法必須執行一次並在類的全部測試以前,發生這種狀況時通常是測試計算共享配置方法,如鏈接到數據庫;

@After :該註釋表示,用其附着的方法在執行每項測試後執行,如執行每個測試後重置某些變量,刪除臨時變量等;

@AfterClass :該註釋表示,當須要執行全部的測試在JUnit測試用例類後執行,AfterClass註解可使用以清理創建方法,如斷開數據庫鏈接,注意:附有此批註(相似於BeforeClass)的方法必須定義爲靜態;

@Ignore :該註釋表示,當想暫時禁用特定的測試執行可使用忽略註釋,每一個被註解爲@Ignore的方法將不被執行。

/
* JUnit 註解示例
*/
@Test
public void testYeepay(){
  Syetem.out.println("用@Test標示測試方法!");
}
@AfterClass
public static void paylus(){
  Syetem.out.println("用@AfterClass標示的方法在測試用例類執行完以後!");
}

3.2 斷言

在這裏,做者將介紹一些斷言方法,全部這些方法都來自 org.junit.Assert 類,其擴展了 java.lang.Object 類併爲它們提供編寫測試,以便檢測故障。簡而言之,咱們就是經過斷言方法來判斷實際結果與咱們預期的結果是否相同,若是相同,則測試成功,反之,則測試失敗。

void assertEquals([String message], expected value, actual value) :斷言兩個值相等,值的類型能夠爲int、short、long、byte、char 或者
java.lang.Object,其中第一個參數是一個可選的字符串消息;
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) :斷言預期數組和結果數組相等,數組的類型能夠爲int、long、short、char、byte 或者 java.lang.Object

4 JUnit 3.X 和 JUnit 4.X 的區別

4.1 JUnit 3.X

(1)使用 JUnit 3.X 版本進行單元測試時,測試類必需要繼承於 TestCase 父類; (2)測試方法須要遵循的原則:

① public的; ② void的; ③ 無方法參數; ④方法名稱必須以 test 開頭;

(3)不一樣的測試用例之間必定要保持徹底的獨立性,不能有任何的關聯;

(4)要掌握好測試方法的順序,不能依賴於測試方法本身的執行順序。

/
* 用 JUnit 3.X 進行測試
*/
import junit.framework.Assert;
import junit.framework.TestCase;
public class TestOperation extends TestCase {
  private Operation operation;
  public TestOperation(String name) { // 構造函數
    super(name);
  }
  @Override
  public void setUp() throws Exception { // 在每一個測試方法執行 [以前] 都會被調用,多用於初始化
    System.out.println("歡迎使用Junit進行單元測試...");
    operation = new Operation();
  }
  @Override
  public void tearDown() throws Exception { // 在每一個測試方法執行 [以後] 都會被調用,多用於釋放資源
    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("除數不能爲 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() 方法功能同樣,在每一個測試方法以前執行,多用於初始化;

@After 註解:與 JUnit 3.X 中的 tearDown() 方法功能同樣,在每一個測試方法以後執行,多用於釋放資源;

@Test(timeout = xxx) 註解:設置當前測試方法在必定時間內運行完,不然返回錯誤;

@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() { // 在全部方法執行以前執行
    System.out.println("@BeforeClass標註的方法,在全部方法執行以前執行...");
  }
  @AfterClass
  public static void globalDestory() { // 在全部方法執行以後執行
    System.out.println("@AfterClass標註的方法,在全部方法執行以後執行...");
  }
  @Before
  public void setUp() { // 在每一個測試方法以前執行
    System.out.println("@Before標註的方法,在每一個測試方法以前執行...");
    operation = new Operation();
  }
  @After
  public void tearDown() { // 在每一個測試方法以後執行
    System.out.println("@After標註的方法,在每一個測試方法以後執行...");
  }
  @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 方法,除數爲 0 的狀況...");
    operation.divide(6, 0);
    fail("test Error");
  }
  public static void main(String[] args) {
  }
}

4.3 特別提醒

經過以上兩個例子,咱們已經能夠大體知道 JUnit 3.X 和 JUnit 4.X 兩個版本的區別啦!

首先,若是咱們使用 JUnit 3.X,那麼在咱們寫的測試類的時候,必定要繼承 TestCase 類,可是若是咱們使用 JUnit 4.X,則不需繼承 TestCase 類,直接使用註解就能夠啦!

在 JUnit 3.X 中,還強制要求測試方法的命名爲「 testXxxx 」這種格式;

在 JUnit 4.X 中,則不要求測試方法的命名格式,但做者仍是建議測試方法統一命名爲「 testXxxx 」這種格式,簡潔明瞭。

此外,在上面的兩個示例中,咱們只給出了測試類,可是在這以前,還應該有一個被測試類,也就是咱們真正要實現功能的類。如今,做者將給出上面示例中被測試的類,即 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;
/
 * 一、建立一個測試類,繼承TestCase類
 */
public class SimpleTestDemo extends TestCase {
  public SimpleTestDemo(String name) {
    super(name);
  }
  /
   * 二、寫一個測試方法,斷言指望的結果
   */
  public void testEmptyCollection(){
    Collection collection = new ArrayList();
    assertTrue(collection.isEmpty());
  }
  /
   * 三、寫一個suite()方法,它會使用反射動態的建立一個包含全部的testXxxx方法的測試套件
   */
  public static Test suit(){
    return new TestSuite(SimpleTestDemo.class);
  }
  /
   * 四、寫一個main()方法,以文本運行器的方式方便的運行測試
   */
  public static void main(String[] args) {
    junit.textui.TestRunner.run(suit());
  }
}

6 我的建議

有些童鞋可能會有一些誤解,認爲寫測試代碼沒有用,並且還會增大本身的壓力,浪費時間。但事實上,寫測試代碼與否,仍是有很大區別的,若是是在小的項目中,或許這種區別還不太明顯,但若是在大型項目中,一旦出現錯誤或異常,用人力去排查的話,那將會浪費不少時間,並且還不必定排查的出來,可是若是用測試代碼的話,JUnit 就是自動幫咱們判斷一些代碼的結果正確與否,從而節省的時間將會遠遠超過你寫測試代碼的時間。

所以,我的建議:要養成編寫測試代碼的習慣,碼一點、測一點;再碼一點,再測一點,如此循環。在咱們不斷編寫與測試代碼的過程當中,咱們將會對類的行爲有一個更爲深刻的瞭解,從而能夠有效的提升咱們的工做效率。下面,做者就給出一些具體的編寫測試代碼的技巧和較好的實踐方法:

1. 不要用 TestCase 的構造函數初始化 Fixture,而要用 setUp() 和 tearDown() 方法; 2. 不要依賴或假定測試運行的順序,由於 JUnit 會利用 Vector 保存測試方法,因此不一樣的平臺會按不一樣的順序從 Vector 中取出測試方法; 3. 避免編寫有反作用的 TestCase,例如:若是隨後的測試依賴於某些特定的交易數據,就不要提交交易數據,只須要簡單的回滾就能夠了; 4. 當繼承一個測試類時,記得調用父類的 setUp() 和 tearDown() 方法; 5. 將測試代碼和工做代碼放在一塊兒,同步編譯和更新; 6. 測試類和測試方法應該有一致的命名方案,如在工做類名前加上 test 從而造成測試類名; 7. 確保測試與時間無關,不要使用過時的數據進行測試,以致於致使在隨後的維護過程當中很難重現測試; 8. 若是編寫的軟件面向國際市場,那麼編寫測試時必定要考慮國際化的因素; 9. 儘量地利用 JUnit 提供地 assert 和 fail 方法以及異常處理的方法,其可使代碼更爲簡潔; 10. 測試要儘量地小,執行速度快; 11. 不要硬性規定數據文件的路徑; 12. 使用文檔生成器作測試文檔。

8 大單元測試框架

1.Arquillian

Arquillian是一個基於JVM的高度可擴展的測試平臺,容許開發人員建立Java的自動化集成,功能和驗收測試。Arquillian容許你在運行態時執行測試。Arquillian可用於管理容器(或容器)的生命週期,綁定測試用例,依賴類和資源。它還可以將壓縮包部署到容器中,並在容器中執行測試並捕獲結果並建立報告。

Arquillian集成了熟悉的測試框架,如JUnit 四、TestNG 5,並容許使用現有的IDE啓動測試。而且因爲其模塊化設計,它可以運行Ant和Maven測試插件。Arquillian目的是簡化項目集成測試和功能測試的編寫,讓它們能像單元測試同樣簡單。

2.JTEST

JTest也被稱爲「Parasoft JTest」,是Parasoft公司生產的自動化Java軟件測試和靜態分析軟件。 JTest包括用於單元測試用例生成和執行,靜態代碼分析,數據流靜態分析和度量分析,迴歸測試,運行時錯誤檢測的功能。

還能夠進行結對的代碼審查流程自動化和運行時錯誤檢測,例如:條件,異常,資源和內存泄漏,安全攻擊漏洞等。

3.The Grinder

「The Grinder」是一個Java負載測試框架。而且經過使用大量負載注射器來爲分佈式測試提供便利。Grinder能夠對具備Java API的任何內容加載測試。這包括HTTP Web服務器,SOAP、REST Web服務、應用程序服務器,包括自定義協議。測試腳本用強大的Jython和Clojure語言編寫。Grinder的GUI控制檯容許對多個負載注射器進行監控和控制,並自動管理客戶端鏈接和Cookie,SSL,代理感知和鏈接限制。您能夠在這裏找到關於磨牀功能的更多深刻信息。

4.TestNG

TestNG受JUnit和NUnit的啓發,是爲Java編程語言而設計的測試框架。TestNG主要設計用於覆蓋更普遍的測試類別,如單元,功能,端到端,集成等。它還引入了一些新功能,使其更強大,更易於使用,如:註解,運行在大線程池中進行各類策略測試,多線程安全驗證代碼測試,靈活的測試配置,數據驅動的參數測試支持等等。

TestNG有各類工具和插件(如Eclipse,IDEA,Maven等)支持。

5.JUnit

JUnit是爲Java編程語言設計的單元測試框架。JUnit在測試驅動開發框架的開發中發揮了重要做用。它是單元測試框架之一,統稱爲由SUnit起源的xUnit。

6.JWalk

JWalk被設計爲用於Java編程語言的單元測試工具包。它被設計爲支持稱爲「Lazy系統單元測試」的測試範例。

JWalkTester工具對任何由程序員提供的編譯的Java類執行任何測試。它可以經過靜態和動態分析以及來自程序員的提示來測試懶惰Lazy規範的一致性。

7.Mockito

Mockito被設計爲用於Java的開源測試框架,MIT許可證。Mockito容許程序員爲了測試驅動開發(TDD)或行爲驅動開發(BDD)而在自動化單元測試中建立和測試雙對象(Mock對象)。

8 Powermock

PowerMock是用於對源代碼進行單元測試的Java框架,它能夠做爲其餘模擬框架的擴展,好比原型Mockito或EasyMock,但具備更強大的功能。PowerMock利用自定義的類加載器和字節碼操縱器來實現靜態方法,構造函數,最終類和方法以及私有方法等的模擬。它主要是爲了擴展示有的API,使用少許的方法和註解來實現額外的功能。

參考文章

https://www.jianshu.com/p/0530cb31c3b2 https://blog.csdn.net/Dream_Weave/article/details/83859750 https://blog.csdn.net/qq_26295547/article/details/83145642 https://www.jianshu.com/p/0530cb31c3b2 http://www.sohu.com/a/145107423_731023

微信公衆號

黃小斜

黃小斜是跨考軟件工程的 985 碩士,自學 Java 兩年,拿到了 BAT 等近十家大廠 offer,從技術小白成長爲阿里工程師。

做者專一於 JAVA 後端技術棧,熱衷於分享程序員乾貨、學習經驗、求職心得和程序人生,目前黃小斜的CSDN博客有百萬+訪問量,知乎粉絲2W+,全網已有10W+讀者。

黃小斜是一個斜槓青年,堅持學習和寫做,相信終身學習的力量,但願和更多的程序員交朋友,一塊兒進步和成長!

原創電子書: 關注微信公衆號【黃小斜】後回覆【原創電子書】便可領取我原創的電子書《菜鳥程序員修煉手冊:從技術小白到阿里巴巴Java工程師》這份電子書總結了我2年的Java學習之路,包括學習方法、技術總結、求職經驗和麪試技巧等內容,已經幫助不少的程序員拿到了心儀的offer!

程序員3T技術學習資源: 一些程序員學習技術的資源大禮包,關注公衆號後,後臺回覆關鍵字 「資料」 便可免費無套路獲取,包括Java、python、C++、大數據、機器學習、前端、移動端等方向的技術資料。

Java技術江湖

若是你們想要實時關注我更新的文章以及分享的乾貨的話,能夠關注個人微信公衆號【Java技術江湖】

這是一位阿里 Java 工程師的技術小站。做者黃小斜,專一 Java 相關技術:SSM、SpringBoot、MySQL、分佈式、中間件、集羣、Linux、網絡、多線程,偶爾講點Docker、ELK,同時也分享技術乾貨和學習經驗,致力於Java全棧開發!

(關注公衆號後回覆」Java「便可領取 Java基礎、進階、項目和架構師等免費學習資料,更有數據庫、分佈式、微服務等熱門技術學習視頻,內容豐富,兼顧原理和實踐,另外也將贈送做者原創的Java學習指南、Java程序員面試指南等乾貨資源)

Java工程師必備學習資源: 一些Java工程師經常使用學習資源,關注公衆號後,後臺回覆關鍵字 「Java」 便可免費無套路獲取。

個人公衆號

相關文章
相關標籤/搜索