前言
PS:本文主要知識(shí)來(lái)源于《Java測(cè)試驅(qū)動(dòng)開(kāi)發(fā)》這本書(shū)
理論知識(shí)儲(chǔ)備
關(guān)于單元測(cè)試
- 單元測(cè)試并非要取代其他類型的測(cè)試荷辕,而只是縮小其他測(cè)試的范圍
- 單元測(cè)試旨在檢查代碼的內(nèi)部質(zhì)量垃沦,專注于質(zhì)量保證而非質(zhì)量檢查
- 質(zhì)量檢查(QC)的重點(diǎn)是發(fā)現(xiàn)缺陷
- 質(zhì)量保證(QA)的重點(diǎn)是將缺陷消滅在萌芽狀態(tài)
- 單元測(cè)試必須能夠快速運(yùn)行
- 必須明確指出測(cè)試前設(shè)置了哪些條件女阀,測(cè)試將執(zhí)行哪些操作以及期望的結(jié)果。
- 不要讓測(cè)試依賴于其他測(cè)試
- 應(yīng)盡可能在測(cè)試中少用甚至不用基類虑灰。相對(duì)比避免代碼重復(fù),測(cè)試的清晰度更重要
- 一種命名方式:Given部分描述前置條件,When部分描述操作偎漫,Then部分描述期望的結(jié)果
- 測(cè)試必須因預(yù)期的原因而失敗。
關(guān)于TDD(測(cè)試驅(qū)動(dòng)開(kāi)發(fā))
- 概念說(shuō)明:TDD這本書(shū)編寫的測(cè)試不以Test結(jié)尾有缆,而以Spec結(jié)尾象踊,以規(guī)范為目標(biāo)
- 規(guī)范不僅用于驗(yàn)證代碼,還被用作可執(zhí)行的文檔棚壁,最主要的是杯矩,它們還被用作思考和設(shè)計(jì)方式。
- 紅燈-綠燈-重構(gòu)(失敗到成功再到完美)
- 第一次運(yùn)行不能通過(guò):還沒(méi)有創(chuàng)建方法
- 第二次運(yùn)行不能通過(guò):添加了方法袖外,但是沒(méi)有實(shí)現(xiàn)
- 第三次運(yùn)行通過(guò):實(shí)現(xiàn)了與這個(gè)測(cè)試相關(guān)聯(lián)的所有代碼
- TDD迫使我們?cè)敿?xì)地考慮需求和設(shè)計(jì)史隆,編寫整潔而可行的代碼,以及創(chuàng)建可執(zhí)行的需求并頻繁重構(gòu)曼验。
- 以測(cè)試方式編寫的文檔泌射。測(cè)試就是可執(zhí)行的文檔,而TDD是創(chuàng)建和維護(hù)這種文檔的最常用方式鬓照。
- 需要為代碼編寫文檔通常意味著代碼本身寫的不好熔酷。另外,不管你如何努力豺裆,文檔都必然會(huì)過(guò)期拒秘。
- 實(shí)現(xiàn)越簡(jiǎn)單,產(chǎn)品越好留储,維護(hù)也越容易
- 使用TDD時(shí)翼抠,不用預(yù)先定義設(shè)計(jì),相反获讳,不斷編寫并實(shí)現(xiàn)規(guī)范的過(guò)程中阴颖,設(shè)計(jì)通常會(huì)變得清晰(熟練之后的事,熟練掌握TDD之前丐膝,我們必須將需求和測(cè)試分開(kāi)定義)
- 將TDD過(guò)程分解為可重復(fù)的短暫周期量愧,其中每個(gè)階段的持續(xù)時(shí)間通常以分鐘乃至秒計(jì)
- 單元測(cè)試的運(yùn)行時(shí)間多長(zhǎng)算合理呢?沒(méi)有放之四海而皆準(zhǔn)的規(guī)則帅矗,但一條經(jīng)驗(yàn)是:如果時(shí)間超過(guò)10~15秒偎肃,就應(yīng)對(duì)此感到擔(dān)憂,并花時(shí)間對(duì)測(cè)試進(jìn)行優(yōu)化浑此。
關(guān)于Mockito
- 將真實(shí)類轉(zhuǎn)換為模擬類累颂。就像重寫了這個(gè)類,將其所有方法都改為空。
- mock():用于創(chuàng)建模擬對(duì)象紊馏,還可使用when()和given()指定這些模擬對(duì)象的行為
- spy():可用于實(shí)現(xiàn)部分模擬料饥。除非另有說(shuō)明,否則間諜對(duì)象調(diào)用實(shí)際方法朱监。mock()創(chuàng)建一個(gè)完全偽造的對(duì)象岸啡,而spy()使用實(shí)際對(duì)象
- verify():用于檢查調(diào)用方法時(shí)提供的是否是指定參數(shù),是一種斷言赫编。
Eclipse 中JUnit 5的使用
項(xiàng)目右鍵 -> properties -> Java Build path -> Add Library -> Junit -> JUnit 5 -> finish
關(guān)鍵步驟如下圖
image.png
選擇JUnit版本
代碼覆蓋率工具-Jacoco
搜索EclEmma
image.png
注解
注解 | 用途 |
---|---|
@Test | 表明一個(gè)測(cè)試方法 |
@DisplayName | 測(cè)試類或方法的顯示名稱 |
@BeforeEach | 表明在單個(gè)測(cè)試方法運(yùn)行之前執(zhí)行的方法 |
@BeforeAll | 表明在所有測(cè)試方法運(yùn)行之前執(zhí)行的方法 |
@AfterEach | 表明在單個(gè)測(cè)試方法運(yùn)行之后執(zhí)行的方法 |
@AfterAll | 表明在所有測(cè)試方法運(yùn)行之后執(zhí)行的方法 |
@Disabled | 禁用測(cè)試類或方法 |
@Tag | 為測(cè)試類或方法添加標(biāo)簽 |
簡(jiǎn)單示例
class FirstTDDSpec {
private FirstTDD testd;
@BeforeAll
static void initAll() {
System.out.println("@BeforeAll 初始化……");
}
@BeforeEach
void beforeEachTest() {
System.out.println("@BeforeEach 初始化……");
testd = new FirstTDD();
}
@AfterAll
static void destoryAll() {
System.out.println("@AfterAll 所有測(cè)試執(zhí)行完畢巡蘸,執(zhí)行銷毀操作……");
}
@AfterEach
void detory() {
System.out.println("@AfterEach 當(dāng)前測(cè)試執(zhí)行完畢,執(zhí)行銷毀操作……");
}
@Test
void whenTheGameIsStartedTheBoardIsEmpty() {
Assert.assertEquals(0, testd.getNumberOfDiscs());
}
@Test
@DisplayName("第一個(gè)mock測(cè)試方法")
void MyFirstMockTest() {
HelloModel mockHelloModel = Mockito.spy(HelloModel.class);
mockHelloModel.setUserName("jack");
mockHelloModel.setPassword("123456");
Assert.assertEquals("jack", mockHelloModel.getUserName());
}
@Test
void whenDiscOutsideBoardThenRuntimeException() {
int column = -1;
assertThrows(RuntimeException.class, () -> testd.putDiscInColumn(column));
}
}
執(zhí)行結(jié)果