@TOC
手機(jī)用戶請(qǐng)
橫屏獲取最佳閱讀體驗(yàn),REFERENCES中是本文參考的鏈接,如需要鏈接和更多資源,可以關(guān)注其他博客發(fā)布地址。
| 平臺(tái) | 地址 |
|---|---|
| CSDN | https://blog.csdn.net/sinat_28690417 |
| 簡(jiǎn)書 | http://m.itdecent.cn/u/3032cc862300 |
| 個(gè)人博客 | https://yiyuery.club |
基于JUnit從零開始認(rèn)識(shí)單元測(cè)試
JUnit預(yù)備知識(shí)
什么是軟件測(cè)試?
- 軟件測(cè)試是檢查實(shí)際結(jié)果與預(yù)期結(jié)果是否匹配并確保軟件系統(tǒng)無(wú)缺陷的活動(dòng)。
- 軟件測(cè)試還有助于識(shí)別產(chǎn)品與實(shí)際需求不符或是缺失項(xiàng)。
- 測(cè)試活動(dòng)既可以手動(dòng)完成,也可以使用自動(dòng)化工具完成。
- 有些人更喜歡將軟件測(cè)試稱為白盒和黑盒測(cè)試
什么是軟件測(cè)試目標(biāo)?
- 在給定的產(chǎn)品中盡可能多地發(fā)現(xiàn)錯(cuò)誤(或bug)。
- 演示一個(gè)給定的軟件產(chǎn)品與它的需求規(guī)格匹配。
- 使用最小的成本和努力來(lái)驗(yàn)證軟件的質(zhì)量。
- 生成高質(zhì)量的測(cè)試用例,執(zhí)行有效的測(cè)試,并發(fā)布正確和有用的問題報(bào)告。
什么是軟件測(cè)試過(guò)程?
軟件測(cè)試通常分為兩個(gè)主要過(guò)程——驗(yàn)證 & 認(rèn)證。
驗(yàn)證是當(dāng)您的團(tuán)隊(duì)只需要檢查軟件、系統(tǒng)或框架是否符合文檔要求時(shí)的過(guò)程。
認(rèn)證是您的團(tuán)隊(duì)需要驗(yàn)證系統(tǒng)正確性的過(guò)程。在這個(gè)過(guò)程中,您將回顧產(chǎn)品、系統(tǒng),并考慮用戶真正想要什么和已經(jīng)做了什么。
在軟件測(cè)試中,缺陷和錯(cuò)誤之間有區(qū)別,我們應(yīng)該清楚地區(qū)分,以避免誤解問題。
軟件測(cè)試分類
單元測(cè)試這是在開發(fā)人員級(jí)別使用的最基本的測(cè)試,測(cè)試人員專注于單元代碼的單個(gè)部分,而它已經(jīng)從任何外部交互或依賴于任何模塊之前被隔離。這個(gè)測(cè)試要求開發(fā)人員檢查他們編寫的最小代碼單元,并證明單元可以獨(dú)立工作。
如果你聽說(shuō)過(guò)測(cè)試驅(qū)動(dòng)開發(fā)(TDD:Test-Driven Development),單元測(cè)試就不陌生。單元測(cè)試是用來(lái)對(duì)一個(gè)模塊、一個(gè)函數(shù)或者一個(gè)類來(lái)進(jìn)行正確性檢驗(yàn)的測(cè)試工作。比如對(duì)函數(shù)abs(),我們可以編寫出以下幾個(gè)測(cè)試用例:
- 輸入正數(shù),比如1、1.2、0.99,期待返回值與輸入相同;
- 輸入負(fù)數(shù),比如-1、-1.2、-0.99,期待返回值與輸入相反;
- 輸入0,期待返回0;
- 輸入非數(shù)值類型,比如None、[]、{},期待拋出TypeError。
把上面的測(cè)試用例放到一個(gè)測(cè)試模塊里,就是一個(gè)完整的單元測(cè)試。單元測(cè)試通過(guò)后有什么意義呢?
如果單元測(cè)試通過(guò),說(shuō)明我們測(cè)試的這個(gè)函數(shù)能夠正常工作。如果單元測(cè)試不通過(guò),要么函數(shù)有bug,要么測(cè)試條件
輸入不正確,總之,需要修復(fù)使單元測(cè)試能夠通過(guò)。
如果我們對(duì)abs()函數(shù)代碼做了修改,只需要再跑一遍單元測(cè)試,如果通過(guò),說(shuō)明我們的修改不會(huì)對(duì)abs()函數(shù)原有
的行為造成影響,如果測(cè)試不通過(guò),說(shuō)明我們的修改與原有行為不一致,要么修改代碼,要么修改測(cè)試。
這種以測(cè)試為驅(qū)動(dòng)的開發(fā)模式最大的好處就是確保一個(gè)程序模塊的行為符合我們?cè)O(shè)計(jì)的測(cè)試用例。在將來(lái)修改的
時(shí)候,可以極大程度地保證該模塊行為仍然是正確的。
From: 廖雪峰
單元測(cè)試方式
單元測(cè)試可以由兩種方式完成:
-
人工測(cè)試
- 手動(dòng)執(zhí)行測(cè)試用例并不借助任何工具的測(cè)試被稱為人工測(cè)試。?消耗時(shí)間并單調(diào):由于測(cè)試用例是由人力資源執(zhí)行,所以非常緩慢并乏味。
- 人力資源上投資巨大:由于測(cè)試用例需要人工執(zhí)行,所以在人工測(cè)試上需要更多的試驗(yàn)員。
- 可信度較低:人工測(cè)試可信度較低是可能由于人工錯(cuò)誤導(dǎo)致測(cè)試運(yùn)行時(shí)不夠精確。
- 非程式化:編寫復(fù)雜并可以獲取隱藏的信息的測(cè)試的話,這樣的程序無(wú)法編寫。
-
自動(dòng)測(cè)試(借助工具支持并且利用自動(dòng)工具執(zhí)行用例被稱為自動(dòng)測(cè)試。)
- 快速自動(dòng)化運(yùn)行測(cè)試用例時(shí)明顯比人力資源快。
- 人力資源投資較少:測(cè)試用例由自動(dòng)工具執(zhí)行,所以在自動(dòng)測(cè)試中需要較少的試驗(yàn)員。
- 可信度更高:自動(dòng)化測(cè)試每次運(yùn)行時(shí)精確地執(zhí)行相同的操作。
- 程式化:試驗(yàn)員可以編寫復(fù)雜的測(cè)試來(lái)顯示隱藏信息。
JUnit 簡(jiǎn)介
JUnit是一個(gè) Java 編程語(yǔ)言的單元測(cè)試框架。JUnit 在測(cè)試驅(qū)動(dòng)的開發(fā)方面有很重要的發(fā)展,是起源于 JUnit 的一個(gè)統(tǒng)稱為 xUnit 的單元測(cè)試框架之一。
JUnit 促進(jìn)了先測(cè)試后編碼的理念,強(qiáng)調(diào)建立測(cè)試數(shù)據(jù)的一段代碼,可以先測(cè)試,然后再應(yīng)用。這個(gè)方法就好比“測(cè)試一點(diǎn),編碼一點(diǎn),測(cè)試一點(diǎn),編碼一點(diǎn)……”,增加了程序員的產(chǎn)量和程序的穩(wěn)定性,可以減少程序員的壓力和花費(fèi)在排錯(cuò)上的時(shí)間。
特點(diǎn):
- JUnit 是一個(gè)開放的資源框架,用于編寫和運(yùn)行測(cè)試。
- 提供注釋來(lái)識(shí)別測(cè)試方法。
- 提供斷言來(lái)測(cè)試預(yù)期結(jié)果。
- 提供測(cè)試運(yùn)行來(lái)運(yùn)行測(cè)試。
- JUnit 測(cè)試允許你編寫代碼更快,并能提高質(zhì)量。
- JUnit 優(yōu)雅簡(jiǎn)潔。沒那么復(fù)雜,花費(fèi)時(shí)間較少。
- JUnit 測(cè)試可以自動(dòng)運(yùn)行并且檢查自身結(jié)果并提供即時(shí)反饋。所以也沒有必要人工梳理測(cè)試結(jié)果的報(bào)告。
- JUnit 測(cè)試可以被組織為測(cè)試套件,包含測(cè)試用例,甚至其他的測(cè)試套件。
- JUnit 在一個(gè)條中顯示進(jìn)度。如果運(yùn)行良好則是綠色;如果運(yùn)行失敗,則變成紅色。
從零開始搭建JUnit測(cè)試環(huán)境
測(cè)試場(chǎng)景
JUnit是一款優(yōu)秀的開源Java單元測(cè)試框架,也是目前使用率最高最流行的測(cè)試框架,開發(fā)工具Eclipse和IDEA對(duì)JUnit都有很好的支持,JUnit主要用于以下測(cè)試場(chǎng)景。
- 白盒測(cè)試:把測(cè)試對(duì)象看作一個(gè)打開的盒子,程序內(nèi)部的邏輯結(jié)構(gòu)和其他信息對(duì)測(cè)試人?員是公開的;
- 回歸測(cè)試:軟件或環(huán)境修復(fù)或更正后的再測(cè)試;
- 單元測(cè)試:最小粒度的測(cè)試,以測(cè)試某個(gè)功能或代碼塊。一般由程序員來(lái)做,因?yàn)樗枰纼?nèi)部程序設(shè)計(jì)和編碼的細(xì)節(jié);
JUnit GitHub地址:https://github.com/junit-team
環(huán)境搭建
- Spring Boot 2.1.0 RELEASE
- JUnit 4.12
- Maven 3.0.0+
- IDEA 2019.2
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Spring 框架
Spring Boot 框架

核心API
JUnit API TestCase
測(cè)試樣例定義了運(yùn)行多重測(cè)試的固定格式
- int countTestCases() 為被run(TestResult result) 執(zhí)行的測(cè)試案例計(jì)數(shù)
- TestResult createResult() 創(chuàng)建一個(gè)默認(rèn)的 TestResult 對(duì)象
- String getName() 獲取 TestCase 的名稱
- TestResult run() 一個(gè)運(yùn)行這個(gè)測(cè)試的方便的方法,收集由TestResult 對(duì)象產(chǎn)生的結(jié)果
- void run(TestResult result) 在 TestResult 中運(yùn)行測(cè)試案例并收集結(jié)果
- void setName(String name) 設(shè)置 TestCase 的名稱
- void setUp()
創(chuàng)建固定裝置,例如,打開一個(gè)網(wǎng)絡(luò)連接 - void tearDown() 拆除固定裝置,例如,關(guān)閉一個(gè)網(wǎng)絡(luò)連接
- String toString() 返回測(cè)試案例的一個(gè)字符串表示
/*測(cè)試入口*/
public class Ex3Test {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(JunitEx3Test.class,JunitEx3_2Test.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
public class JunitEx3_2Test extends TestCase {
@Test
public void testXX2() {
System.out.println("No of Test2 Case:" + this.countTestCases());
System.out.println("Original Test Name:" + this.getName());
this.setName("JunitEx3_2Test Name");
System.out.println("Update Test Name:" + this.getName());
}
}
public class JunitEx3Test extends TestCase {
/**
* 為被run(TestResult result) 執(zhí)行的測(cè)試案例計(jì)數(shù)
* 注意前綴必須為test***
*/
@Test
public void testX1() {
System.out.println("No of Test Case:" + this.countTestCases());
System.out.println("Original Test Name:" + this.getName());
this.setName("testX1 Name");
System.out.println("Update Test Name:" + this.getName());
}
@Test
public void testX2() {
System.out.println("No of Test Case:" + this.countTestCases());
System.out.println("Original Test Name:" + this.getName());
this.setName("testX2 Name");
System.out.println("Update Test Name:" + this.getName());
}
}
JUnit API TestResult
TestResult 類收集所有執(zhí)行測(cè)試案例的結(jié)果。它是收集參數(shù)層面的一個(gè)實(shí)例。這個(gè)實(shí)驗(yàn)框架區(qū)分失敗和錯(cuò)誤。失敗是可以預(yù)料的并且可以通過(guò)假設(shè)來(lái)檢查。錯(cuò)誤是不可預(yù)料的問題就像 ArrayIndexOutOfBoundsException。
TestResult 類的一些重要方法列式如下:
- void addError(Test test, Throwable t) 在錯(cuò)誤列表中加入一個(gè)錯(cuò)誤
- void addFailure(Test test, AssertionFailedError t) 在失敗列表中加入一個(gè)失敗
- void endTest(Test test) 顯示測(cè)試被編譯的這個(gè)結(jié)果
- int errorCount() 獲取被檢測(cè)出錯(cuò)誤的數(shù)量
- Enumeration errors() 返回錯(cuò)誤的詳細(xì)信息
- int failureCount() 獲取被檢測(cè)出的失敗的數(shù)量
- void run(TestCase test) 運(yùn)行 TestCase
- int int runCount() 獲得運(yùn)行測(cè)試的數(shù)量
- void startTest(Test test) 聲明一個(gè)測(cè)試即將開始
- void stop()標(biāo)明測(cè)試必須停止
/*測(cè)試入口*/
public class Ex4Test {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(JEx4_1Test.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
public class JEx4_1Test extends TestResult {
@Test
public void testX1() throws IllegalArgumentException {
throw new IllegalArgumentException("testX1 failed");
}
@Test
public void testX2() throws IllegalArgumentException {
throw new IllegalArgumentException("testX2 failed");
}
@Test
public void testX3() {
System.out.println("testX3 success...");
}
}
JUnit API TestSuite
TestSuite 類是測(cè)試的組成部分。它運(yùn)行了很多的測(cè)試案例
- void addTest(Test test) 在套中加入測(cè)試。
- void addTestSuite(Class<? extends TestCase> testClass) 將已經(jīng)給定的類中的測(cè)試加到套中。
- int countTestCases() 對(duì)這個(gè)測(cè)試即將運(yùn)行的測(cè)試案例進(jìn)行計(jì)數(shù)。
- String getName() 返回套的名稱。
- void run(TestResult result) 在 TestResult 中運(yùn)行測(cè)試并收集結(jié)果。
- void setName(String name) 設(shè)置套的名稱。
- Test testAt(int index) 在給定的目錄中返回測(cè)試。
- int testCount() 返回套中測(cè)試的數(shù)量。
- static Test warning(String message) 返回會(huì)失敗的測(cè)試并且記錄警告信息。
/*測(cè)試入口*/
public class Ex5Test {
public static void main(String[] args) {
TestSuite suite = new TestSuite(TestJunit1.class, TestJunit2.class);
TestResult result = new TestResult();
suite.run(result);
System.out.println("Number of test cases = " + result.runCount());
}
}
public class TestJunit1 extends TestCase {
String message = "Robert";
MessageUtil messageUtil = new MessageUtil(message);
@Test
public void testMessage() {
System.out.println("TestJunit1 testMessage()");
assertEquals(message, messageUtil.printMessage());
}
}
public class TestJunit2 extends TestCase {
String message = "Robert";
MessageUtil messageUtil = new MessageUtil(message);
@Test
public void testMessage() {
System.out.println("TestJunit2 testMessage()");
message = "Hi!" + "Robert";
assertEquals(message,messageUtil.printMessage());
}
}
JUnit單元測(cè)試如何開展
IDEA支持
IntellJ IDEA 支持快速生成測(cè)試用例
Ctrl + Shift + T

斷言測(cè)試
測(cè)試結(jié)果的表達(dá)式
斷言測(cè)試也就是期望值測(cè)試,是單元測(cè)試的核心,也就是決定測(cè)試結(jié)果的表達(dá)式
Assert對(duì)象中的斷言方法:
Assert.assertEquals 對(duì)比兩個(gè)值相等
Assert.assertNotEquals 對(duì)比兩個(gè)值不相等
Assert.assertSame 對(duì)比兩個(gè)對(duì)象的引用相等
Assert.assertArrayEquals 對(duì)比兩個(gè)數(shù)組相等
Assert.assertTrue 驗(yàn)證返回是否為真
Assert.assertFlase 驗(yàn)證返回是否為假
Assert.assertNull 驗(yàn)證null
Assert.assertNotNull 驗(yàn)證非null
public class Ex1Test {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(Ex1Test.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
/**
* 簡(jiǎn)單輸出測(cè)試 Person(name=xxx)
*/
@Test
public void test1() {
Person person = Person.builder().name("xxx").build();
System.out.println(person.toString());
}
/**
* 利用 Assert 斷言輸出結(jié)果
* org.junit.Assert
*/
@Test
public void test2() {
Person person = Person.builder().name("xxx").build();
System.out.println(person.toString());
Assert.assertEquals(person.getName(), "xxx");
Assert.assertSame(person.getName(), "xxx");
Assert.assertNotSame(person.getName(), "xx2");
Assert.assertFalse(person.getName().endsWith("xx2"));
Assert.assertTrue(person.getName().endsWith("xx"));
Assert.assertNull(null);
Assert.assertNotNull(person);
}
/**
* 測(cè)試 @Before
* Person(name=before....1)
* Person(name=xxx3)
*/
@Test
public void test3() {
Person person = Person.builder().name("xxx3").build();
System.out.println(person.toString());
}
@Before
public void before1() {
Person person = Person.builder().name("before....1").build();
System.out.println(person.toString());
}
/**
* 測(cè)試 @After
* Person(name=before....1)
* Person(name=xxx4)
* Person(name=after...1)
*/
@Test
public void test4() {
Person person = Person.builder().name("xxx4").build();
System.out.println(person.toString());
}
@After
public void after1() {
Person person = Person.builder().name("after...1").build();
System.out.println(person.toString());
}
/**
* 測(cè)試@AfterClass
* Person(name=before....1)
* Person(name=xxx5)
* Person(name=after...1)
* Person(name=after...2)
*/
@Test
public void test5() {
Person person = Person.builder().name("xxx5").build();
System.out.println(person.toString());
}
/**
* java.lang.Exception: Method after2() should be static
* 在所有方法執(zhí)行之后執(zhí)行
*/
@AfterClass
public static void after2() {
Person person = Person.builder().name("after...2").build();
System.out.println(person.toString());
}
/**
* 測(cè)試 @BeforeClass
* Person(name=before...2)
* Person(name=before....1)
* Person(name=xxx6)
* Person(name=after...1)
* Person(name=after...2)
*/
@Test
public void test6() {
Person person = Person.builder().name("xxx6").build();
System.out.println(person.toString());
}
/**
* java.lang.Exception: Method before2() should be static
* 在所有方法執(zhí)行之前執(zhí)行
*/
@BeforeClass
public static void before2() {
Person person = Person.builder().name("before...2").build();
System.out.println(person.toString());
}
/**
* hamcrest-core-1.3.jar
* Hamcrest是一款用以編寫matcher對(duì)象的框架,以類庫(kù)的形式發(fā)布。一個(gè)matcher對(duì)象就是一個(gè)明確定義的匹配規(guī)則.
* > 匹配規(guī)則:Matchers
*/
@Test
@Ignore
public void test7() {
MatcherAssert.assertThat(Long.valueOf(1), instanceOf(Integer.class));
//字符串匹配符
String n = "Magci";
//containsString:字符串變量中包含指定字符串時(shí),測(cè)試通過(guò)
MatcherAssert.assertThat(n, Matchers.containsString("ci"));
//startsWith:字符串變量以指定字符串開頭時(shí),測(cè)試通過(guò)
MatcherAssert.assertThat(n, Matchers.startsWith("Ma"));
//endsWith:字符串變量以指定字符串結(jié)尾時(shí),測(cè)試通過(guò)
MatcherAssert.assertThat(n, Matchers.endsWith("i"));
//euqalTo:字符串變量等于指定字符串時(shí),測(cè)試通過(guò)
MatcherAssert.assertThat(n, Matchers.equalTo("Magci"));
//equalToIgnoringCase:字符串變量在忽略大小寫的情況下等于指定字符串時(shí),測(cè)試通過(guò)
MatcherAssert.assertThat(n, Matchers.equalToIgnoringCase("magci"));
//equalToIgnoringWhiteSpace:字符串變量在忽略頭尾任意空格的情況下等于指定字符串時(shí),測(cè)試通過(guò)
MatcherAssert.assertThat(n, Matchers.equalToIgnoringWhiteSpace(" Magci "));
//...so on
}
@Test
public void testX() {
}
/**
* fail({{message}}) 直接中止方法運(yùn)行
*/
@Test
@Ignore
public void test8() {
Assert.fail("directlly stop and output fail log!");
}
}
套件測(cè)試
測(cè)試套件意味著捆綁幾個(gè)單元測(cè)試用例并且一起執(zhí)行他們。在 JUnit 中,@RunWith 和 @Suite 注釋用來(lái)運(yùn)行套件測(cè)試。這個(gè)教程將向您展示一個(gè)例子,其中含有兩個(gè)測(cè)試樣例 TestJunit1 & TestJunit2 類,我們將使用測(cè)試套件一起運(yùn)行他們。
/*測(cè)試入口*/
public class Ex2Test{
public static void main(String[] args) {
Result result = JUnitCore.runClasses(TestSuite.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
public class MessageUtil {
String message;
public String printMessage() {
return message;
}
public MessageUtil(String message) {
this.message = message;
}
}
public class TestJunit1 {
String message = "Robert";
MessageUtil messageUtil = new MessageUtil(message);
@Test
public void testMessage() {
System.out.println("TestJunit1 testMessage()");
assertEquals(message, messageUtil.printMessage());
}
}
public class TestJunit2 {
String message = "Robert";
MessageUtil messageUtil = new MessageUtil(message);
@Test
public void testMessage() {
System.out.println("TestJunit2 testMessage()");
message = "Hi!" + "Robert";
assertEquals(message,messageUtil.printMessage());
}
}
@RunWith(Suite.class)
@Suite.SuiteClasses({
TestJunit1.class ,TestJunit2.class
})
public class TestSuite {
}
時(shí)間測(cè)試
Junit 提供了一個(gè)暫停的方便選項(xiàng)。如果一個(gè)測(cè)試用例比起指定的毫秒數(shù)花費(fèi)了更多的時(shí)間,那么 Junit 將自動(dòng)將它標(biāo)記為失敗。timeout 參數(shù)和 @Test 注釋一起使用?,F(xiàn)在讓我們看看活動(dòng)中的 @test(timeout)。
public class Ex6Test {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(JEx6_1Test.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
public class JEx6_1Test {
@Test
public void testX1(){
System.out.println("x1");
}
@Test(timeout = 10)
public void testX2(){
for(int i =0;i<100000000;i++){
Math.random();
}
System.out.println("x2");
}
@Test
public void testX3(){
System.out.println("x3");
}
}
異常測(cè)試
Junit 用代碼處理提供了一個(gè)追蹤異常的選項(xiàng)。你可以測(cè)試代碼是否它拋出了想要得到的異常。expected 參數(shù)和 @Test 注釋一起使用。現(xiàn)在讓我們看看活動(dòng)中的 @Test(expected)。
/*測(cè)試入口*/
public class Ex7Test {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(JEx7_1Test.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
public class JEx7_1Test {
String message = "Robert";
MessageUtil messageUtil = new MessageUtil(message);
@Test
public void testX1(){
System.out.println("x1");
}
//@Test(expected = ArithmeticException.class)
@Test(expected = IllegalArgumentException.class)
public void testX2(){
messageUtil.printMessage();
}
@Test
public void testX3(){
System.out.println("x3");
}
}
class MessageUtil {
private String message;
//Constructor
//@param message to be printed
public MessageUtil(String message){
this.message = message;
}
// prints the message
public void printMessage(){
System.out.println(message);
int a =0;
int b = 1/a;
}
// add "Hi!" to the message
public String salutationMessage(){
message = "Hi!" + message;
System.out.println(message);
return message;
}
}
參數(shù)化測(cè)試
Junit 4 引入了一個(gè)新的功能參數(shù)化測(cè)試。參數(shù)化測(cè)試允許開發(fā)人員使用不同的值反復(fù)運(yùn)行同一個(gè)測(cè)試。你將遵循 5 個(gè)步驟來(lái)創(chuàng)建參數(shù)化測(cè)試。
- 用 @RunWith(Parameterized.class) 來(lái)注釋 test 類。
- 創(chuàng)建一個(gè)由 @Parameters 注釋的公共的靜態(tài)方法,它返回一個(gè)對(duì)象的集合(數(shù)組)來(lái)作為測(cè)試數(shù)據(jù)集合。
- 創(chuàng)建一個(gè)公共的構(gòu)造函數(shù),它接受和一行測(cè)試數(shù)據(jù)相等同的東西。
- 為每一列測(cè)試數(shù)據(jù)創(chuàng)建一個(gè)實(shí)例變量。
- 用實(shí)例變量作為測(cè)試數(shù)據(jù)的來(lái)源來(lái)創(chuàng)建你的測(cè)試用例。
一旦每一行數(shù)據(jù)出現(xiàn)測(cè)試用例將被調(diào)用。
/*測(cè)試入口*/
public class Ex8Test {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(JEx8_1Test.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
@RunWith(Parameterized.class)
public class JEx8_1Test {
private Integer inputNumber;
private Boolean expectedResult;
private PrimeNumberChecker primeNumberChecker;
@Before
public void initialize() {
primeNumberChecker = new PrimeNumberChecker();
}
public JEx8_1Test(Integer inputNumber,
Boolean expectedResult) {
this.inputNumber = inputNumber;
this.expectedResult = expectedResult;
}
@Parameterized.Parameters
public static Collection primeNumbers() {
return Arrays.asList(new Object[][] {
{ 2, true },
{ 6, false },
{ 19, true },
{ 22, false },
{ 23, true }
});
}
@Test
public void testPrimeNumberChecker() {
System.out.println("Parameterized Number is : " + inputNumber);
assertEquals(expectedResult,
primeNumberChecker.validate(inputNumber));
}
}
/**
* 質(zhì)數(shù)
*/
class PrimeNumberChecker {
public Boolean validate(final Integer primeNumber) {
for (int i = 2; i < (primeNumber / 2); i++) {
if (primeNumber % i == 0) {
return false;
}
}
return true;
}
}
事務(wù)控制
public class Ex1Test extends BaseBootJunitTest {
@Resource
private IPersonService personService;
/**
* 初始數(shù)據(jù)插入
*/
@Test
public void testX1() {
personService.saveWithName("p_test_1111");
}
/**
* 方法內(nèi)部事務(wù)間隔離
*/
@Test
public void testX2() {
//第一行數(shù)據(jù)插入成功
personService.saveWithName(CapsuleStringUtil.randomStr("p_test_"));
//第二行數(shù)據(jù)插入失敗
personService.saveNoTransactional("p_test_1111");
}
/**
* 針對(duì)Test方法加入事務(wù)控制
* 避免測(cè)試數(shù)據(jù)污染數(shù)據(jù)庫(kù)
*/
@Test
@Transactional(rollbackFor = Exception.class)
public void testX3() {
//第一行數(shù)據(jù)插入失敗
personService.saveWithName(CapsuleStringUtil.randomStr("p_test_"));
//第二行數(shù)據(jù)插入失敗
personService.saveNoTransactional("p_test_1111");
}
}
接口測(cè)試
public class Ex2Test extends BaseBootJunitTest {
@Resource
private TestRestTemplate restTemplate;
@Test
public void testHello() {
String resp = restTemplate.getForObject("/hello", String.class);
System.out.println(resp);
Assert.assertEquals("Hello Junit!", resp);
}
}
忽略測(cè)試

框架擴(kuò)展
Hamcrest匹配器的用法
官網(wǎng)地址 http://hamcrest.org/JavaHamcrest/tutorial
自動(dòng)化測(cè)試
/*測(cè)試入口*/
public class Ex3Test {
public static void main(String[] args) {
Result result = JUnitCore.runClasses(JEx3_1Test.class);
for (Failure failure : result.getFailures()) {
System.out.println(failure.toString());
}
System.out.println(result.wasSuccessful());
}
}
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class JEx3_1Test extends BaseBootJunitTest {
@Resource
private IPersonService personService;
private String pName;
/**
* 每個(gè)測(cè)試方法都是個(gè)測(cè)試用例,獨(dú)立執(zhí)行 @beforeInit 注解標(biāo)注的方法
*/
@Before
public void before() {
System.out.println("[Before]-----------------------------------");
pName = "p_test_auto_1";
}
@After
public void after() {
System.out.println("[After]-----------------------------------");
}
/**
* 正常過(guò)程:人員添加
*/
@Test
public void testX() {
Person person = personService.saveWithName(pName);
log("添加后人員信息返回結(jié)果", person);
Person dbPerson = personService.findByPersonName(pName,true);
Assert.assertNotNull(dbPerson);
}
/**
* 正常過(guò)程:人員修改
*/
@Test
public void testX2() {
Person oldPerson = personService.findByPersonName(pName,true);
oldPerson.setName(pName + "_modify");
Person newPerson = personService.saveWithResult(oldPerson);
log("修改后人員信息", newPerson);
Assert.assertNotNull(newPerson);
Assert.assertEquals(oldPerson.getName(), newPerson.getName());
}
/**
* 正常過(guò)程:人員刪除
*/
@Test
public void testX3() {
Person dbPerson = personService.findByPersonName(pName+"_modify",true);
personService.delete(dbPerson.getId());
log("刪除前人員信息", dbPerson);
Person personDel = personService.findByPersonName(dbPerson.getName(), false);
log("刪除后搜索結(jié)果", personDel);
}
}
總結(jié)
本文從軟件測(cè)試為討論的切入點(diǎn),介紹了單元測(cè)試在軟件測(cè)試中的重要性和對(duì)應(yīng)角色。接下來(lái),以JUnit測(cè)試框架展開,就環(huán)境搭建、測(cè)試類型、JUnit 核心API、JUnit各種測(cè)試方法分別進(jìn)行了介紹,并提供了代碼示例。最后,結(jié)合人員的增刪改操作,編寫了對(duì)應(yīng)的自動(dòng)化測(cè)試用例。
REFRENCES
更多
掃碼關(guān)注“架構(gòu)探險(xiǎn)之道”,回復(fù)本文標(biāo)題或關(guān)鍵詞,獲取本文源碼
知識(shí)星球(掃碼加入獲取歷史源碼和文章資源鏈接)