详解介绍JUnit单元测试框架(完整版)
(一)JUnit介紹
目錄
(一)JUnit介紹
1.什么是單元測試?
2.什么是單元測試框架?
3.什么是JUnit?
(二)JUnit 安裝
1.IntelliJ IDEA 安裝 Junit
2.Maven 安裝 Junit
(三)JUnit 編寫單元測試
1.編寫單元測試
2.測試功能模塊
(四)JUnit 注解
1.JUnit 注解
2.例子
(五)JUnit 注解之Fixture
什么是Fixture
JUnit 中的 Fixture
(六)JUnit 用例執(zhí)行順序
@FixMethodOrder
例子
?(七)JUnit 斷言方法
JUnit 斷言方法
例子
(八)JUnit 測試批量運(yùn)行
IntelliJ IDEA 中設(shè)置運(yùn)行
通過測試套件運(yùn)行
(九)JUnit5 介紹與安裝
JUnit5 介紹
Maven 安裝
(十)JUnit5 創(chuàng)建測試
創(chuàng)建測試用例
(十一)JUnit5 新的用法
(十二)補(bǔ)充:JUnit 注解之Rule
使用框架自帶的Rule
自定義的Rule
1.什么是單元測試?
單元測試負(fù)責(zé)對最小的軟件設(shè)計(jì)單元(模塊)進(jìn)行驗(yàn)證,根據(jù)軟件設(shè)計(jì)文檔中對模塊功能的描述,對重要的程序分支進(jìn)行測試并發(fā)現(xiàn)錯(cuò)誤。
2.什么是單元測試框架?
對于單元測試框架來講,它主要完成以下幾件事。
提供用例組織與執(zhí)行:測試用例只有幾條時(shí),可以不考慮用例組織,但是用例達(dá)到成百上千時(shí),大量的測試用例堆砌在一起,就產(chǎn)生了擴(kuò)展性與維護(hù)性等問題
提供豐富的斷言方法:不論是功能測試,還是單元測試,在用例執(zhí)行完之后都需要將實(shí)際結(jié)果與預(yù)期結(jié)果相比較(斷言),從而斷定用例是否執(zhí)行通過。單元測試框架一般提供豐富的斷言方法。例如:判斷相等/不等、包含/不包含、True/False的斷言方法等
提供豐富的日志:?當(dāng)測試用例執(zhí)行失敗時(shí)能拋出清晰的失敗原因,當(dāng)所有用例執(zhí)行完成后能提供豐富的執(zhí)行結(jié)果。例如,總執(zhí)行時(shí)間、失敗用例數(shù)、成功用例數(shù)等。
從這些特性來看單元測試框架的作用是:幫助我們更自動(dòng)化完成測試,所以,它是自動(dòng)化測試的基礎(chǔ)。
3.什么是JUnit?
Junit 官網(wǎng):http://junit.org/
JUnit 是一個(gè)編寫可重復(fù)測試的簡單框架。它是單元測試框架的 xUnit 架構(gòu)的一個(gè)實(shí)例。
(二)JUnit 安裝
Junit目前分兩個(gè)版本,Junit4 和 Junit5 , 本系列教程打算從 Junit4 開始介紹,最后,再介紹 Junit5 有哪些新特性
1.IntelliJ IDEA 安裝 Junit
Java 開發(fā)的同學(xué),推薦使用 IntelliJ IDEA,推薦閱讀《IntelliJ IDEA 教程》。
1、下載?junit-4.12.jar?文件:https://github.com/junit-team/junit4/releases
2、 打開 IntelliJ IDEA ,菜單欄:File菜單 –> Porject Structure 選項(xiàng) –> Dependencies 標(biāo)簽 –> 點(diǎn)擊 “+” 號 –> Library… –> Java 。?選擇下載的?junit-4.12.jar?進(jìn)行添加。
3、以同樣的方式下載和導(dǎo)入?hamcrest:?https://github.com/hamcrest/JavaHamcrest/releases?,否則,你將無法運(yùn)行 Junit 單元測試。
2.Maven 安裝 Junit
相比較而言,Maven 的安裝要簡單很多,打開你 Maven 項(xiàng)目中的 pom.xml 文件,添加如下配置:
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope> </dependency>更多的 Maven 項(xiàng)目,可以登錄:https://www.mvnrepository.com?網(wǎng)站查找。
(三)JUnit 編寫單元測試
1.編寫單元測試
創(chuàng)建 JunitDemo 類,編寫第一個(gè)單元測試用例。
import static org.junit.Assert.assertEquals; import org.junit.Test;public class JunitDemo {@Testpublic void myFirstTest() {assertEquals(2+2, 4);}}@Test?用來注釋一個(gè)普通的方法為一條測試用例。
assertEquals()?方法用于斷言兩個(gè)值是否相關(guān)。
2.測試功能模塊
創(chuàng)建一個(gè)被測試類:Count ,代碼如下:
public class Count {/*** 計(jì)算并返回兩個(gè)參數(shù)的和*/public int add(int x ,int y){return x + y;} }Count 類的實(shí)現(xiàn)非常簡單,看注釋就可以了。
接下來,創(chuàng)建 CountTest 類,用于測試 Count 類。
import static org.junit.Assert.assertEquals; import org.junit.Test;public class CountTest {@Testpublic void testAdd() {Count count = new Count();int result = count.add(2,2);assertEquals(result, 4);}}new?出 Count 類,調(diào)用 add() 方法并傳參,通過 assertEquals() 斷言 返回結(jié)果。
恭喜! 你已經(jīng)會編寫單元測試了。
(四)JUnit 注解
1.JUnit 注解
JUnit 注解說明:
| @Test: | 標(biāo)識一條測試用例。 (A)?(expected=XXEception.class) ? (B)?(timeout=xxx) |
| @Ignore:? | 忽略的測試用例。 |
| @Before:? | 每一個(gè)測試方法之前運(yùn)行。 |
| @After :? | 每一個(gè)測試方法之后運(yùn)行。 |
| @BefreClass | ?所有測試開始之前運(yùn)行。 |
| @AfterClass? | 所有測試結(jié)果之后運(yùn)行。 |
2.例子
創(chuàng)建被測試類 Count .
public class Count {/*** 計(jì)算并返回兩個(gè)參數(shù)的和*/public int add(int x ,int y){return x + y;}/*** 計(jì)算并返回兩個(gè)數(shù)相除的結(jié)果*/public int division(int a, int b){return a / b;} }創(chuàng)建測試類 CountTest .
import static org.junit.Assert.assertEquals;import org.junit.Ignore; import org.junit.Test;public class CountTest {//驗(yàn)證超時(shí)@Test(timeout=100)public void testAdd() throws InterruptedException {Thread.sleep(101);new Count().add(1, 1);}//驗(yàn)證拋出異常@Test(expected=ArithmeticException.class)public void testDivision() {new Count().division(8, 0);}// 跳過該條用例@Ignore@Testpublic void testAdd2() {Count count = new Count();int result = count.add(2,2);assertEquals(result, 5);}}1、在 testAdd() 用例中設(shè)置?timeout=100?, 說明的用例的運(yùn)行時(shí)間不能超過 100 毫秒, 但故意在用例添加 sleep() 方法休眠 101 毫秒,所以會導(dǎo)致用例失敗。
2、在 Java 中被除數(shù)不能為0,所以?8?0?會報(bào) ArithmeticException 異常, 在 @Test 中設(shè)置?expected=ArithmeticException.class?,說明拋該異常符合預(yù)期。
3、@Ignore?表來標(biāo)識該用例跳過,不管用例運(yùn)行成功還是失敗。
執(zhí)行結(jié)果如下:
(五)JUnit 注解之Fixture
繼續(xù)介紹 JUnit 的注解
什么是Fixture
Test Fixture 是指一個(gè)測試運(yùn)行所需的固定環(huán)境,準(zhǔn)確的定義:
The test fixture is everything we need to have in place to exercise the SUT
在進(jìn)行測試時(shí),我們通常需要把環(huán)境設(shè)置成已知狀態(tài)(如創(chuàng)建對象、獲取資源等)來創(chuàng)建測試,每次測試開始時(shí)都處于一個(gè)固定的初始狀態(tài);測試結(jié)果后需要將測試狀態(tài)還原,所以,測試執(zhí)行所需要的固定環(huán)境稱為 Test Fixture。
JUnit 中的 Fixture
被測試類同樣使用上一小節(jié)的 Count , 創(chuàng)建 TestFixture 測試類。
import static org.junit.Assert.*; import org.junit.*;public class TestFixture {//在當(dāng)前測試類開始時(shí)運(yùn)行。@BeforeClasspublic static void beforeClass(){System.out.println("-------------------beforeClass");}//在當(dāng)前測試類結(jié)束時(shí)運(yùn)行。@AfterClasspublic static void afterClass(){System.out.println("-------------------afterClass");}//每個(gè)測試方法運(yùn)行之前運(yùn)行@Beforepublic void before(){System.out.println("=====before");}//每個(gè)測試方法運(yùn)行之后運(yùn)行@Afterpublic void after(){System.out.println("=====after");}@Testpublic void testAdd1() {int result=new Count().add(5,3);assertEquals(8,result);System.out.println("test Run testadd1");}@Testpublic void testAdd2() {int result=new Count().add(15,13);assertEquals(28,result);System.out.println("test Run testadd2");}}代碼中的注釋已經(jīng)對?@BeforeClass、 @AfterClass 、 @Before 、 @After?做了說明。
至于什么時(shí)候會用到這些方法跟你具體的業(yè)務(wù)用例有關(guān),如果是 Web UI 自動(dòng)化測試,可以把 瀏覽器驅(qū)動(dòng)的定義放到?@Before中,瀏覽器的關(guān)閉放到?@After?中。
運(yùn)行結(jié)果如下:
(六)JUnit 用例執(zhí)行順序
在運(yùn)行測試的過程中,有時(shí)候需要控制用例的執(zhí)行順序。
@FixMethodOrder
JUnit 通過?@FixMethodOrder?注解來控制測試方法的執(zhí)行順序的。@FixMethodOrder?注解的參數(shù)是?org.junit.runners.MethodSorters?對象,在枚舉類?org.junit.runners.MethodSorters?中定義了如下三種順序類型:
- MethodSorters.JVM
?Leaves the test methods in the order returned by the JVM. Note that the order from the JVM may vary from run to run (按照J(rèn)VM得到的方法順序,也就是代碼中定義的方法順序)
- MethodSorters.DEFAULT(默認(rèn)的順序)
Sorts the test methods in a deterministic, but not predictable, order() (以確定但不可預(yù)期的順序執(zhí)行)
- MethodSorters.NAME_ASCENDING
Sorts the test methods by the method name, in lexicographic order, with Method.toString() used as a tiebreaker (按方法名字母順序執(zhí)行)
例子
具體如何使用,看例子,創(chuàng)建 TestRunSequence 測試類。
import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; import static org.junit.Assert.assertEquals;// 按字母順序執(zhí)行 @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestRunSequence {@Testpublic void TestCase1() {assertEquals(2+2, 4);}@Testpublic void TestCase2() {assertEquals(2+2, 4);}@Testpublic void TestAa() {assertEquals("hello", "hi");} }MethodSorters.NAME_ASCENDING?設(shè)置按字母的順序執(zhí)行,所以,TestAa()?先被執(zhí)行,雖然它在代碼中是最后一條用例。
運(yùn)行結(jié)果如下:
?(七)JUnit 斷言方法
JUnit 斷言方法
JUnit 所提供的斷言方法:
| assertArrayEquals(expecteds, actuals) | 查看兩個(gè)數(shù)組是否相等。 |
| assertEquals(expected, actual) | 查看兩個(gè)對象是否相等。類似于字符串比較使用的equals()方法。 |
| assertNotEquals(first, second) | 查看兩個(gè)對象是否不相等。 |
| assertNull(object) | 查看對象是否為空。 |
| assertNotNull(object) | 查看對象是否不為空。 |
| assertSame(expected, actual) | 查看兩個(gè)對象的引用是否相等。類似于使用“==”比較兩個(gè)對象。 |
| assertNotSame(unexpected, actual) | 查看兩個(gè)對象的引用是否不相等。類似于使用“!=”比較兩個(gè)對象。 |
| assertTrue(condition) | 查看運(yùn)行結(jié)果是否為true。 |
| assertFalse(condition) | 查看運(yùn)行結(jié)果是否為false。 |
| assertThat(actual, matcher) | 查看實(shí)際值是否滿足指定的條件。 |
| fail() | 讓測試失敗。 |
例子
關(guān)于斷言方法,我們前面用得最多的是?assertEquals?,用于斷言兩個(gè)對象是否相等。這里再介紹一個(gè)?assertTrue?的使用。
創(chuàng)建 AssertTest 測試類(包了含被測試方法):
import org.junit.*; import static org.junit.Assert.*;public class AssertTest {/*** 判斷一個(gè)數(shù)是否為素?cái)?shù)*/public static Boolean Prime(int n) {for (int i = 2; i < Math.sqrt(n); i++) {if (n % i == 0) {return false;}}return true;}@Testpublic void testPrime(){int n = 7;assertTrue(AssertTest.Prime(n));}}Prime()?方法用于判斷一個(gè)數(shù)是否為素?cái)?shù)(只能被1和它本身整除的數(shù)),并返回 True 或 False ,在測試用例中通過?assertTrue來斷言結(jié)果。
(八)JUnit 測試批量運(yùn)行
前面測試用例的運(yùn)行 主要針對單個(gè)測試類進(jìn)行的,當(dāng)然,在 IntelliJ IDEA 中也可以選擇單個(gè)的方法執(zhí)行。那如果我們想運(yùn)行所有的用例的文件呢?
IntelliJ IDEA 中設(shè)置運(yùn)行
設(shè)置
在 IntelliJ IDEA 中,菜單欄:Run菜單 –> Edit Configurations…選項(xiàng)。
在 Junit 目錄下,選擇任意一個(gè)用例文件。
- Test Kind : 選擇用例的運(yùn)行類型/級別。
- packages : 選擇用例運(yùn)行的目錄,即你的測試用例目錄。
設(shè)置完成后,點(diǎn)擊?“OK”?按鈕。
運(yùn)行
點(diǎn)擊 IntelliJ IDEA 工具欄上的運(yùn)行按鈕,來運(yùn)行 test 目錄下的所有用例。
運(yùn)行結(jié)果:
通過測試套件運(yùn)行
這種方法引入一種?“測試套件”?的概念,JUnit 提供了一種批量運(yùn)行測試類的方法,叫測試套件。
測試套件的寫法需要遵循以下原則:
創(chuàng)建一個(gè)空類作為測試套件的入口;
使用注解?org.junit.runner.RunWith?和?org.junit.runners.Suite.SuitClasses?修飾這個(gè)空類。
將?org.junit.runners.Suite?作為參數(shù)傳入給注解?RunWith,以提示 JUnit 為此類測試使用套件運(yùn)行器執(zhí)行。
將需要放入此測試套件的測試類組成數(shù)組作為注解 SuiteClasses 的參數(shù)。
保證這個(gè)空類使用public修飾,而且存在公開的不帶任何參數(shù)的構(gòu)造函數(shù)。
單獨(dú)創(chuàng)建一個(gè)測試類 runAllTest .
package test;import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses;@RunWith(Suite.class) @SuiteClasses({CountTest.class,TestFixture.class,AssertTest.class,TestRunSequence.class, }) public class runAllTest {}把需要運(yùn)行的測試類放到?SuiteClasses?中,運(yùn)行 runAllTest 測試類,即可批量執(zhí)行測試用例。
(九)JUnit5 介紹與安裝
官方網(wǎng)址:http://junit.org/junit5/
Junit5 已經(jīng)不算是新的版本了,2016 年推出非正式版,相比較 JUnit4 安裝和使用都有一定的差異。
JUnit5 介紹
The new major version of the programmer-friendly testing framework for Java 8
一個(gè)新的重要版本,程序員更友好的測試框架,基于 Java8。
關(guān)于
JUnit5 是 JUnit 的下一代。我們的目標(biāo)是為 JVM 上的開發(fā)人員端測試創(chuàng)建一個(gè)最新的基礎(chǔ)。這包括針對 Java 8 及以上,以及使許多不同風(fēng)格的測試。
Junit5 組成
先看來個(gè)公式:
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
這看上去比 Junit4 復(fù)雜,實(shí)際上在導(dǎo)入包時(shí)也會復(fù)雜一些。
-
JUnit Platform 是在JVM上啟動(dòng)測試框架的基礎(chǔ)。
-
JUnit Jupiter 是JUnit5擴(kuò)展的新的編程模型和擴(kuò)展模型,用來編寫測試用例。Jupiter子項(xiàng)目為在平臺上運(yùn)行Jupiter的測試提供了一個(gè)TestEngine (測試引擎)。
-
JUnit Vintage 提供了一個(gè)在平臺上運(yùn)行 JUnit3 和 JUnit4 的 TestEngine 。
Maven 安裝
首先,你需要通過 IntelliJ?IDEA 創(chuàng)建一個(gè) Maven 項(xiàng)目,IntelliJ?IDEA 集成的有 Maven,所以,你很容易做到這一點(diǎn)。通過 Maven 的 pom.xml 文件,添加 Junit5 。
pom.xml 文件配置如下:
<dependencies><dependency><groupId>org.junit.platform</groupId><artifactId>junit-platform-launcher</artifactId><version>1.0.1</version><scope>test</scope></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-engine</artifactId><version>5.0.1</version><scope>test</scope></dependency><dependency><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId><version>4.12.1</version><scope>test</scope></dependency></dependencies>(十)JUnit5 創(chuàng)建測試
創(chuàng)建測試用例
我在 IntelliJ?IDEA 中創(chuàng)建的 Maven 項(xiàng)目,目錄結(jié)構(gòu)如下:
在 test.java 目錄下創(chuàng)建一個(gè) FistJUnit5Tests 類。代碼如下:
import static org.junit.jupiter.api.Assertions.assertEquals;import org.junit.jupiter.api.Test;class FirstJUnit5Tests {@Testvoid myFirstTest() {assertEquals(2, 1 + 1);}}明顯看出和 Junit4 還是有些不同的。
首先,導(dǎo)入測試測試注解(@Test)和斷言方法(assertEquals)的路徑不同。
其次,不需要手動(dòng)把測試和測試方法聲明為 public 了。
(十一)JUnit5 新的用法
創(chuàng)建 JUnit5NewTests 測試類。
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertAll;import org.junit.jupiter.api.*;class JUnit5NewTests {@BeforeEach@DisplayName("每條用例開始時(shí)執(zhí)行")void start(){}@AfterEach@DisplayName("每條用例結(jié)束時(shí)執(zhí)行")void end(){}@Testvoid myFirstTest() {assertEquals(2, 1 + 1);}@Test@DisplayName("描述測試用例╯°□°)╯")void testWithDisplayName() {}@Test@Disabled("這條用例暫時(shí)跑不過,忽略!")void myFailTest(){assertEquals(1,2);}@Test@DisplayName("運(yùn)行一組斷言")public void assertAllCase() {assertAll("groupAssert",() -> assertEquals(2, 1 + 1),() -> assertTrue(1 > 0));}@Test@DisplayName("依賴注入1")public void testInfo(final TestInfo testInfo) {System.out.println(testInfo.getDisplayName());}@Test@DisplayName("依賴注入2")public void testReporter(final TestReporter testReporter) {testReporter.publishEntry("name", "Alex");}}用法都已經(jīng)通過測試用例的?@DisplayName?進(jìn)行了說明,這里不再解釋。
運(yùn)行結(jié)果如下:
(十二)補(bǔ)充:JUnit 注解之Rule
一個(gè)JUnit Rule就是一個(gè)實(shí)現(xiàn)了TestRule的類,這些類的作用類似于?@Before、@After,是用來在每個(gè)測試方法的執(zhí)行前后執(zhí)行一些代碼的一個(gè)方法。 那為什么不直接用這些?@Before、@After呢?這是因?yàn)樗鼈兌贾荒茏饔糜谝粋€(gè)類,如果同一個(gè)setup需要在兩個(gè)類里面同時(shí)使用,那么你就要在兩個(gè)測試類里面定義相同的@Before方法,然后里面寫相同的代碼,這就造成了代碼重復(fù)。
此外,JUnit Rule還能做一些?@Before、@After這些注解做不到的事情,那就是他們可以動(dòng)態(tài)的獲取將要運(yùn)行的測試類、測試方法的信息。
使用框架自帶的Rule
除了增加Rule特性,新版JUnit還添加了很多核心Rule
- TemporaryFolder:測試可以創(chuàng)建文件與目錄并且會在測試運(yùn)行結(jié)束后將其刪除。這對于那些與文件系統(tǒng)打交道且獨(dú)立運(yùn)行的測試來說很有用。
- ExternalResource:這是一種資源使用模式,它會提前建立好資源并且會在測試結(jié)束后將其銷毀。這對于那些使用socket、嵌入式服務(wù)器等資源的測試來說很有用。
- ErrorCollector:可以讓測試在失敗后繼續(xù)運(yùn)行并在測試結(jié)束時(shí)報(bào)告所有錯(cuò)誤。這對于那些需要驗(yàn)證大量獨(dú)立條件的測試來說很有用(盡管這本身可能是個(gè)“test smell”)。
- ExpectedException:可以在測試中指定期望的異常類型與消息。
- Timeout:為類中的所有測試應(yīng)用相同的超時(shí)時(shí)間。
例如,TimeOut這個(gè)Rule的使用。
import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout;public class RuleTestDemo {//使用Timeout這個(gè)Rule@Rulepublic Timeout timeout = new Timeout(1000); @Testpublic void testMethod1() throws Exception {Thread.sleep(1001);}@Testpublic void testMethod2() throws Exception {Thread.sleep(999);} }使用JUnit所提供的Timeout類,該類用于控制測試用例的執(zhí)行超時(shí)時(shí)間。這里設(shè)置為1秒,當(dāng)用例執(zhí)行超過1秒則失敗。接下來分別在 testMethod1和testMethod2兩個(gè)用例中使用sleep()方法來控制用例的執(zhí)行時(shí)間,顯然testMethod1超過1秒,則運(yùn)行失敗。
自定義的Rule
除了可以使用JUnit框架自帶的Rule,還可以根據(jù)自己的需求自定義Rule。簡單來說,自定義一個(gè)Rule就是implement一個(gè)TestRule?接口,并實(shí)現(xiàn)apply()方法。該方法需要返回一個(gè)Statement對象。例子如下:
import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement;public class MethodNameRule implements TestRule {public Statement apply(final Statement base, final Description description) {return new Statement() {@Overridepublic void evaluate() throws Throwable {//在測試方法運(yùn)行之前做一些事情,在base.evaluate()之前String className = description.getClassName();String methodName = description.getMethodName();base.evaluate(); //運(yùn)行測試方法//在測試方法運(yùn)行之后做一些事情,在base.evaluate()之后System.out.println("Class name:"+className+", method name: "+methodName);}};} }這里實(shí)現(xiàn)的功能是在每次測試用例運(yùn)行之后,打印當(dāng)前測試用例的類名和方法名。 在上面的例子中添加這里定義的MethodNameRule 。
…… public class RuleTestDemo {//使用Timeout這個(gè)Rule@Rulepublic Timeout timeout = new Timeout(1000); //使用自定義Rule,@Rulepublic MethodNameRule methodNameRule = new MethodNameRule();……再次運(yùn)行測試用例,執(zhí)行結(jié)果如下:
?
總結(jié)
以上是生活随笔為你收集整理的详解介绍JUnit单元测试框架(完整版)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu10的pci扩展卡驱动安装失
- 下一篇: 手写原笔迹输入_原笔迹手写软件 - 随意