摘要:一旦你完成了相應函數,只需要把注解刪去,就可以進行正常的測試。表示該方法只執行一次,并且在所有方法之后執行。
測試類型 單元測試(Unit test)
單元測試關注單一的類. 它們存在的目的是檢查這個類中的代碼是否按照期望正確運行.
集成測試(Integration test)顧名思義, 集成測試是檢查開發的模塊和其他模塊整合時是否正常工作.
雖然集成測試的代碼影響范圍比單元測試要廣, 但是集成測試和單元測試一樣, 也是針對于開發者而言的.
端到端測試是將整個系統作為一個整體, 然后從用戶的角度進行測試的.
端到端測試的目的是測試系統在實際使用的是否正常的, 因此通常來說是不需要測試替身的(Test Double)
單元測試的目的: 測試當前所寫的代碼是否是正確的, 例如輸入一組數據, 會輸出期望的數據; 輸入錯誤數據, 會產生錯誤異常等.
在單元測試中, 我們需要保證被測系統是獨立的(SUT 沒有任何的 DOC), 即當被測系統通過測試時, 那么它在任何環境下都是能夠正常工作的. 編寫單元測試時, 僅僅需要關注單個類就可以了. 而不需要關注例如數據庫服務, Web 服務等組件.
被測系統(System under test, SUT)表示正在被測試的系統, 目的是測試系統能否正確操作.
根據測試類型的不同, SUT 指代的內容也不同, 例如 SUT 可以是一個類甚至是一整個系統.
被測系統所依賴的組件, 例如進程 UserService 的單元測試時, UserService 會依賴 UserDao, 因此 UserDao 就是 DOC.
測試替身(Test Double)一個實際的系統會依賴多個外部對象, 但是在進行單元測試時, 我們會用一些功能較為簡單的并且其行為和實際對象類似的假對象來作為 SUT 的依賴對象, 以此來降低單元測試的復雜性和可實現性. 在這里, 這些假對象就被稱為 測試替身(Test Double).
測試替身有如下 5 種類型:
Test stub, 為 SUT 提供數據的假對象.
我們舉一個例子來展示什么是 Test stub.
假設我們的一個模塊需要從 HTTP 接口中獲取商品價格數據, 這個獲取數據的接口被封裝為 getPrice 方法. 在對這個模塊進行測試時, 我們顯然不太可能專門開一個 HTTP 服務器來提供此接口, 而是提供一個帶有 getPrice 方法的假對象, 從這個假對象中獲取數據.
在這個例子中, 提供數據的假對象就叫做 Test stub.
Fake object
實現了簡單功能的一個假對象. Fake object 和 Test stub 的主要區別就是 Test stub 側重于用于提供數據的假對象, 而 Fake object 沒有這層含義.
使用 Fake object 的最主要的原因就是在測試時某些組件不可用或運行速度太慢, 因而使用 Fake object 來代替它們.
Mock object
用于模擬實際的對象, 并且能夠校驗對這個 Mock object 的方法調用是否符合預期.
實際上, Mock object 是 Test stub 或 Fake object 一種, 但是 Mock object 有 Test stub/Fake object 沒有的特性, Mock object 可以很靈活地配置所調用的方法所產生的行為, 并且它可以追蹤方法調用, 例如一個 Mock Object 方法調用時傳遞了哪些參數, 方法調用了幾次等.
Dummy object: 在測試中并不使用的, 但是為了測試代碼能夠正常編譯/運行而添加的對象. 例如我們調用一個 Test Double 對象的一個方法, 這個方法需要傳遞幾個參數, 但是其中某個參數無論是什么值都不會影響測試的結果, 那么這個參數就是一個 Dummy object.
Dummy object 可以是一個空引用, 一個空對象或者是一個常量等.
簡單的說, Dummy object 就是那些沒有使用到的, 僅僅是為了填充參數列表的對象.
Test Spy
可以包裝一個真實的 Java 對象, 并返回一個包裝后的新對象. 若沒有特別配置的話, 對這個新對象的所有方法調用, 都會委派給實際的 Java 對象.
mock 和 spy 的區別是: mock 是無中生有地生出一個完全虛擬的對象, 它的所有方法都是虛擬的; 而 spy 是在現有類的基礎上包裝了一個對象, 即如果我們沒有重寫 spy 的方法, 那么這些方法的實現其實都是調用的被包裝的對象的方法.
Test fixture所謂 test fixture, 就是運行測試程序所需要的先決條件(precondition). 即對被測對象進行測試時鎖需要的一切東西(The test fixture is everything we need to have in place to exercise the SUT). 這個 東西 不單單指的是數據, 同時包括對被測對象的配置, 被測對象所需要的依賴對象等.
JUnit4 之前是通過 setUp, TearDown 方法完成, 在 JUnit4這, 我們可以使用@Before 代替 setUp 方法, @After 代替 tearDown 方法.
注意, @Before 在每個測試方法運行前都會被調用, @After 在每個測試方法運行后都會被調用.
因為 @Before 和 @After 會在每個測試方法前后都會被調用, 而有時我們僅僅需要在測試前進行一次初始化, 這樣的情況下, 可以使用@BeforeClass 和@AfterClass 注解.
測試用例(Test case)在 JUnit 3中, 測試方法都必須以 test 為前綴, 且必須是 public void 的, JUnit 4之后, 就沒有這個限制了, 只要在每個測試方法標注 @Test 注解, 方法簽名可以是任意的.
測試套件通過 TestSuit 對象將多個測試用例組裝成一個測試套件, 測試套件批量運行.
通過@RunWith 和@SuteClass 兩個注解, 我們可以創建一個測試套件. 通過@RunWith 指定一個特殊的運行器, 幾 Suite.class 套件運行器, 并通過@SuiteClasses 注解, 將需要進行測試的類列表作作為參數傳入.
我們已一個簡單的例子來快速展示 JUnit4 的基本用法.
首先新建一個名為 JUniTest 的 Maven 工程, 然后添加依賴:
junit junit 4.12
接著編寫測試套件:
import org.junit.Test; import static org.junit.Assert.assertEquals; public class TestJunit { @Test public void testingCrunchifyAddition() { assertEquals("Here is test for Addition Result: ", 30, addition(27, 3)); } @Test public void testingHelloWorld() { assertEquals("Here is test for Hello World String: ", "Hello + World", helloWorld()); } public int addition(int x, int y) { return x + y; } public String helloWorld() { String helloWorld = "Hello +" + " World"; return helloWorld; } }
隨后使用測試用例:
public class App { public static void main(String[] args) { Result result = JUnitCore.runClasses(TestJunit.class); for (Failure failure : result.getFailures()) { System.out.println(failure.toString()); } if (result.wasSuccessful()) { System.out.println("Both Tests finished successfully..."); } } }
這就是一個完整的 JUnit 測試例子了.
定義測試一個 JUnit 測試是一個在專用于測試的類中的一個方法, 并且這個方法被 @org.junit.Test 注解標注. 例如:
public class TestJunit { @Test public void testingCrunchifyAddition() { assertEquals("Here is test for Addition Result: ", 30, addition(27, 3)); } ... }JUnit4 生命周期
JUnit4測試用例的完整的生命周期要經歷如下幾個階段:
類級初始化資源處理
方法級初始化資源處理
執行測試用例中的方法
方法級銷毀資源處理
類級銷毀資源處理
其中, 類級初始化和銷毀資源處理在每一個測試用例類這僅僅執行一次, 方法級初始化, 銷毀資源處理方法在執行測試用例這的每個測試方法中都會被執行一次.
JUnit4 注解@Test (expected = Exception.class) 表示預期會拋出Exception.class 的異常
@Ignore 含義是“某些方法尚未完成,暫不參與此次測試”。這樣的話測試結果就會提示你有幾個測試被忽略,而不是失敗。一旦你完成了相應函數,只需要把@Ignore注解刪去,就可以進行正常的測試。
@Test(timeout=100) 表示預期方法執行不會超過 100 毫秒,控制死循環
@Before 表示該方法在每一個測試方法之前運行,可以使用該方法進行初始化之類的操作
@After 表示該方法在每一個測試方法之后運行,可以使用該方法進行釋放資源,回收內存之類的操
@BeforeClass 表示該方法只執行一次,并且在所有方法之前執行。一般可以使用該方法進行數據庫連接操作,注意該注解運用在靜態方法。
@AfterClass 表示該方法只執行一次,并且在所有方法之后執行。一般可以使用該方法進行數據庫連接關閉操作,注意該注解運用在靜態方法。
@Test 注解被@Test 標注的方法就是執行測試用例的測試方法, 例如:
public class TestJunit { @Test public void myTest() { assertEquals("Here is test for Addition Result: ", 30, addition(27, 3)); } }
方法myTest 被注解@Test 標注, 表示這個方法是一個測試方法, 當運行測試用例時, 會自動調用這個方法 .
@BeforeClass , @AfterClass, @Before, @After使用@BeforeClass 和 @AfterClass 兩個注解標注的方法會在所有測試方法執行前后各執行一次
使用@Before 和 @After 兩個注解標注的方法會在每個測試方法執行前后都執行一次.
如果有多個測試類, 可以合并成一個測試套件進行測試, 運行一個 Test Suite, 那么就會運行在這個 Test Suite 中的所用的測試.
例如:
import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith( Suite.class ) @SuiteClasses( { JUnitTest1.class, JUnitTest2.class } ) public class AllTests { }
在這個例子中, 我們定義了一個 Test Suite, 這個 Test Suite 包含了兩個測試類: JUnitTest1 和 JUnitTest2, 因此運行 這個 Test Suite 時, 就會自動運行這兩個測試類了.
在 IntelliJ IDEA 中使用 JUnit 4創建一個名為 JUnitTest 的 HelloWorld 工程, 添加依賴:
junit junit 4.11
然后創建 src/tests, 在File -> Project Structure -> Modules -> Sources 中, 右鍵選中 tests, 將其設置為 Test, 此時 tests 目錄就變為綠色:
然后創建需要進行測試的類:
public class HelloWorld { public String sayHello() { return "Hello World!"; } }
在 HelloWorld 類名上按下 alt + enter 后, 就可以自動生成測試類了:
IntelliJ 在生tests/ 目錄下生成了一個測試類, 我們可以添加自動測試內容:
import org.junit.Test; import static org.junit.Assert.assertEquals; public class HelloWorldTest { @Test public void testSayHello() throws Exception { HelloWorld helloWorld = new HelloWorld(); assertEquals(helloWorld.sayHello(), "Hello World!"); } }
本文由 yongshun 發表于個人博客, 采用署名-非商業性使用-相同方式共享 3.0 中國大陸許可協議.
非商業轉載請注明作者及出處. 商業轉載請聯系作者本人
Email: yongshun1228@gmail.com
本文標題為: Java JUnit 單元測試小結
本文鏈接為: https://segmentfault.com/a/11...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65081.html
摘要:寫單元測試時,應該把這些依賴隔離,讓每個單元保持獨立。以上的各種原因,都會影響單元測試的結果。在單元測試的基礎上,將相關模塊組合成為子系統或系統進行測試,稱為集成測試。可以看到,單元測試速度比集成測試,也叫測試要快,并且開發成本也是最低。 showImg(/img/remote/1460000006811144); 原文鏈接:http://www.jianshu.com/p/bc996...
摘要:我們寫單元測試,一般都會用到一個或多個單元測試框架,在這里,我們介紹一下這個測試框架。除了幫我們找出所有的測試方法,并且方便運行意外,單元測試框架還幫我們做了其他事情。 我們寫單元測試,一般都會用到一個或多個單元測試框架,在這里,我們介紹一下JUnit4這個測試框架。這是Java界用的最廣泛,也是最基礎的一個框架,其他的很多框架,包括我們后面會看到的Robolectric,都是基于或兼...
摘要:所以,寫單元測試,就是給你的每個類的每個方法寫對于的測試方法。常見的單元測試框架有等等。那么我們給這個東西做單元測試的時候,不是測這一整個流程。叫做集成測試,而不是單元測試。那對于這個例子,單元測試是怎么樣的呢這個請看下一小節。 這是一系列安卓單元測試的文章,目測主要會cover以下的主題: 什么是單元測試 為什么要做單元測試 JUnit Mockito Robolectric Da...
閱讀 3528·2021-11-18 10:02
閱讀 3103·2019-08-29 18:34
閱讀 3389·2019-08-29 17:00
閱讀 420·2019-08-29 12:35
閱讀 747·2019-08-28 18:22
閱讀 1910·2019-08-26 13:58
閱讀 1659·2019-08-26 10:39
閱讀 2668·2019-08-26 10:11