在今天的文章中打算和大家聊一聊關(guān)于測(cè)試的話題,也許有朋友會(huì)問(wèn),作為一名碼農(nóng)為什么要關(guān)注測(cè)試的問(wèn)題?我們把代碼開(kāi)發(fā)完基本自測(cè)沒(méi)問(wèn)題了,扔給測(cè)試不就行了?有問(wèn)題再改唄!也許有很多人都會(huì)這么想,的確,目前國(guó)內(nèi)很多程序員并不太關(guān)注Unit Test,很多互聯(lián)網(wǎng)公司也并沒(méi)有強(qiáng)制要求開(kāi)發(fā)人員必須編寫(xiě)Unit Test Case。究其原因,可能是國(guó)內(nèi)公司都比較有錢(qián),測(cè)試團(tuán)隊(duì)動(dòng)輒幾十人,甚至上百人的公司大有人在。所以,從很多程序員的心態(tài)上看,測(cè)試這么多,直接扔給他們測(cè)試就好了!而另外一個(gè)被提及的原因,則是國(guó)內(nèi)互聯(lián)網(wǎng)公司產(chǎn)品迭代速度太快,需求太多做不過(guò)來(lái),那里有時(shí)間寫(xiě)Unit Test呢?
創(chuàng)新互聯(lián)于2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目網(wǎng)站設(shè)計(jì)、成都網(wǎng)站設(shè)計(jì)網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元忻州做網(wǎng)站,已為上家服務(wù),為忻州各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:18982081108也許原因是多樣的,但拋開(kāi)各種各樣的因素,今天我們只從程序員成長(zhǎng)的角度來(lái)聊一聊該不該寫(xiě)Unit Test?最近這段時(shí)間和海外的程序員朋友合作開(kāi)發(fā)項(xiàng)目比較多,給我的感受是他們特別強(qiáng)調(diào)Unit Test,用他們的話來(lái)說(shuō)比較在意程序的品質(zhì)。而反觀國(guó)內(nèi)很多公司這一點(diǎn)做的就并不是那么好了!之前也和他們聊過(guò)其中的原因,他們認(rèn)為是國(guó)內(nèi)最近這些年的發(fā)展太快了,以至于有些過(guò)程被跳過(guò)了。
我們知道開(kāi)發(fā)一個(gè)軟件或者平日里在現(xiàn)有的項(xiàng)目中開(kāi)發(fā)某個(gè)需求時(shí),嚴(yán)格來(lái)說(shuō)一般會(huì)經(jīng)歷這么一個(gè)流程,如下圖所示:
從圖上可以看到,在這個(gè)流程中軟件被交付集成測(cè)試之前,一定要先跑過(guò)Unit Test,而現(xiàn)在很多國(guó)內(nèi)公司的測(cè)試流程都繞過(guò)Unit Test直接過(guò)度到集成測(cè)試和QA測(cè)試,而從客觀的情況看,其實(shí)往往開(kāi)發(fā)對(duì)邏輯是最了解的,如果開(kāi)發(fā)可以通過(guò)覆蓋相對(duì)完整的Unit Test的話,實(shí)際上后續(xù)測(cè)試流程就會(huì)順利的多,而且寫(xiě)Unit Test還有一個(gè)好處,就是能夠促使開(kāi)發(fā)人員不斷優(yōu)化代碼的設(shè)計(jì)邏輯,因?yàn)橐坏┠惆l(fā)現(xiàn)代碼無(wú)法被Unit Test的時(shí)候,就說(shuō)明你的代碼不夠組件化而需要被重構(gòu)了!作為一名程序員,如果你能夠在這種過(guò)程中不斷地審視自己寫(xiě)過(guò)的代碼,相信你的代碼編寫(xiě)水平一定會(huì)得到不斷地提高!
而從軟件可維護(hù)性的角度看,Unit Test覆蓋全面的項(xiàng)目往往都會(huì)比較好維護(hù),因?yàn)橥暾腢nit Test實(shí)際上已經(jīng)固化了軟件當(dāng)前的邏輯,一旦有人在后續(xù)的開(kāi)發(fā)中破壞了這個(gè)邏輯,就會(huì)導(dǎo)致Unit Test無(wú)法通過(guò),此時(shí)如果要求無(wú)法被Unit Test跑過(guò)的代碼不能被編譯成功或者提交的話,那么就會(huì)強(qiáng)迫修改者去完善Unit Test。這樣也從側(cè)面提高了程序員的測(cè)試意識(shí),減少了發(fā)生重大Bug的幾率!
從以上兩個(gè)角度看,Unit Test一方面可以提高程序員的編碼水平,另外一方面也能盡量保證軟件的質(zhì)量,所以Unit Test是一件非常有價(jià)值的事情,難怪他們說(shuō)優(yōu)秀的程序員20%的時(shí)間都在寫(xiě)Unit Test!
在前面的內(nèi)容中,我們講到Unit Test是一件非常有價(jià)值的事情,那么在實(shí)際的項(xiàng)目中Unit Test到底該怎么寫(xiě)呢?以使用Spring Boot框架并基于Spring MVC開(kāi)發(fā)的Web服務(wù)為例,大部分情況下的代碼結(jié)構(gòu)如圖所示:
在這個(gè)軟件結(jié)構(gòu)中一般面向外部調(diào)用的是Controller層的服務(wù)接口定義,這一層由Spring MVC框架提供支持;而Controller層在接收到請(qǐng)求后需要將參數(shù)傳遞給Service層的業(yè)務(wù)方法進(jìn)行處理,而Service層的業(yè)務(wù)方法邏輯就會(huì)比較多樣,例如可能需要操作數(shù)據(jù)庫(kù)就通過(guò)Dao層提供的組件去實(shí)現(xiàn),也可能需要訪問(wèn)個(gè)中間件組件之類(lèi),如緩存服務(wù)Redis、消息服務(wù)RocketMQ之類(lèi)。除此之外,Service層邏輯可能還會(huì)涉及到其他第三方服務(wù)的調(diào)用,例如支付業(yè)務(wù)還需要調(diào)用支付寶之類(lèi)的接口等等!
所以一般來(lái)說(shuō)Unit Test的重點(diǎn)就是Service層的業(yè)務(wù)邏輯方法,如果Controller層也涉及到一些流程邏輯之類(lèi),也需要被Unit Test覆蓋一下!而具體的Unit Test用例編寫(xiě),遵循Maven工程規(guī)約即可。
不過(guò)說(shuō)到這里大家可能會(huì)有很大的疑問(wèn),那就是我們?cè)谶M(jìn)行Unit Test時(shí),正如上圖所示Service層本身依賴了很多其他組件,有些需要調(diào)用數(shù)據(jù)庫(kù)、有些需要訪問(wèn)Redis、有些還需要調(diào)用第三方接口,在這種情況下好像很難讓Unit Test跑下去,因?yàn)椴豢赡苊看芜\(yùn)行Unit Test的時(shí)候這些環(huán)境都是在線的,怎么辦呢?所以在早期寫(xiě)Unit Test,如果有第三方依賴無(wú)法被測(cè)試的情況下是需要我們手動(dòng)編寫(xiě)Mock測(cè)試代碼的,舉個(gè)例子假設(shè)我們有個(gè)業(yè)務(wù)層的類(lèi)class A{...}需要被Unit Test,但是A中依賴于第三方組件代碼B,由于B需要連接外部網(wǎng)絡(luò),所以我們?cè)跍y(cè)試A的時(shí)候沒(méi)有辦法直接依賴B的實(shí)例,所以我們一般來(lái)說(shuō)需要單獨(dú)定義個(gè)class MockB extend B{@Override ...},這個(gè)類(lèi)繼承B并以Mock的方式重寫(xiě)其方法,從而來(lái)為A類(lèi)的Unit Test提供Mock Bean!而這種由于組件依賴復(fù)雜的情況,也在某種程度上限制來(lái)大家寫(xiě)Unit Test的熱情,不過(guò)下面要介紹的這個(gè)神器會(huì)讓這件事變得非常容易!
在上面我們談到了在編寫(xiě)業(yè)務(wù)層Unit Test時(shí)候會(huì)發(fā)現(xiàn)復(fù)雜的組件依賴需要我們編寫(xiě)很多額外的Mock類(lèi),增加來(lái)我們編寫(xiě)Unit Test的難度,而Mockito這個(gè)測(cè)試框架的出現(xiàn)則讓Mock這件事變得非常容易了!Mockito是一個(gè)模擬測(cè)試框架,可以讓我們以注解(@MockBean)的方式優(yōu)雅地進(jìn)行依賴組件的Mock并對(duì)執(zhí)行邏輯進(jìn)行驗(yàn)證。使用Mockito的一般步驟如下:
如果我們?cè)赟pring Boot的工程中引入了測(cè)試依賴Jar,實(shí)際上就已經(jīng)引入了Junit及Mockito這兩組測(cè)試框架的依賴。如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
下面我們以一個(gè)實(shí)際的案例來(lái)演示下如何編寫(xiě)一個(gè)針對(duì)Service層代碼Unit Test,Service業(yè)務(wù)邏輯代碼如下:
@Service
public class UserAccountTradeServiceImpl implements UserAccountTradeService {
@Autowired
WalletOrderDao walletOrderDao;
@Autowired
PaymentClient paymentClient;
@Override
public AccountChargeTradeResVo accountChargeTrade(AccountChargeTradeReqVo accountChargeTradeReqVo)
throws Exception {
//充值交易防重
WalletOrder walletOrder = walletOrderDao.selectOrderById(accountChargeTradeReqVo.getOrderId());
if (walletOrder != null) {
throw new Exception("充值訂單重復(fù)");
}
//構(gòu)建充值訂單
walletOrder = WalletOrder.builder().orderId(accountChargeTradeReqVo.getOrderId())
.userId(String.valueOf(accountChargeTradeReqVo.getUserId()))
.amount(accountChargeTradeReqVo.getAmount())
.busiType("0").tradeType("charge").currency(accountChargeTradeReqVo.getCurrency()).status("1")
.isRenew(accountChargeTradeReqVo.getReNew()).tradeTime(new Timestamp(new Date().getTime()))
.updateTime(new Timestamp(new Date().getTime()))
.build();
walletOrderDao.insertOrder(walletOrder);
//調(diào)用支付接口
paymentClient.consumeAccount(1, "1", "CNY");
//構(gòu)建返回參數(shù)
AccountChargeTradeResVo accountChargeTradeResVo = AccountChargeTradeResVo.builder()
.userId(Long.valueOf(walletOrder.getUserId())).currency(walletOrder.getCurrency())
.orderId(walletOrder.getOrderId()).businessType(walletOrder.getBusiType()).build();
return accountChargeTradeResVo;
}
}
以上業(yè)務(wù)代碼實(shí)際上是演示了一個(gè)用戶錢(qián)包充值的大致邏輯的業(yè)務(wù)層方法,而該方法中有兩個(gè)依賴組件需要被Mock一個(gè)是表示操作數(shù)據(jù)庫(kù)的walletOrderDao,另外一個(gè)則是表示需要調(diào)用支付系統(tǒng)的客戶端依賴paymentClient。那么使用Mockito該如何在Unit Test中進(jìn)行Mock呢?
我們?cè)诠こ虒?duì)應(yīng)的test目錄的包結(jié)構(gòu)中,建立一個(gè)與業(yè)務(wù)層邏輯包結(jié)構(gòu)一樣的測(cè)試代碼結(jié)構(gòu),如下圖所示:
一般來(lái)說(shuō)Unit Test類(lèi)的代碼接口與實(shí)際源碼結(jié)構(gòu)一致就行,以被測(cè)試類(lèi)+Test后綴命名即可。接下來(lái)我們編寫(xiě)該測(cè)試代碼:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {UserAccountTradeServiceImpl.class})
//@ActiveProfiles({"test"})
public class UserAccountTradeServiceImplTest {
@MockBean
private WalletOrderDao walletOrderDao;
@MockBean
private PaymentClient paymentClient;
@Autowired
private UserAccountTradeServiceImpl userAccountTradeServiceImpl;
@Test
public void accountChargeTradeTest() throws Exception {
AccountChargeTradeReqVo accountChargeTradeReqVo = AccountChargeTradeReqVo.builder().orderId("12345")
.userId(1001).amount(1000).currency("CNY").tradeTime("2019070412102023").reNew("1").build();
AccountChargeTradeResVo accountChargeTradeResVo = userAccountTradeServiceImpl
.accountChargeTrade(accountChargeTradeReqVo);
assertNotNull(accountChargeTradeResVo);
assertEquals(accountChargeTradeResVo.getOrderId(), accountChargeTradeReqVo.getOrderId());
given(paymentClient.consumeAccount(any(Long.class), any(String.class), any(String.class))).willReturn(null);
verify(paymentClient).consumeAccount(any(Long.class), any(String.class), any(String.class));
}
}
在以上測(cè)試代碼中我們通過(guò)@MockBean這個(gè)注解就很容易的Mock了該業(yè)務(wù)層代碼的依賴組件,這樣測(cè)試代碼在執(zhí)行依賴組件的邏輯時(shí)就會(huì)被Mock而不會(huì)真正調(diào)用這個(gè)方法。而一般情況下我們也可以驗(yàn)證下Mock對(duì)象的方法是否有被調(diào)用,但是只是驗(yàn)證下調(diào)用本身是否觸發(fā)而并不是真的調(diào)用,可以使用given/verify這兩個(gè)Mocktio提供的方法來(lái)實(shí)現(xiàn)。
對(duì)于大部分情況采用這樣的模式進(jìn)行Unit Test就差不多了,更多其他細(xì)節(jié)的用法大家可以在好好研究下Mocktio提供的功能!在這里示例中還有個(gè)一個(gè)小的技巧,就是我們?cè)谑褂聾SpringBootTest的時(shí)候如:
@SpringBootTest(classes = {UserAccountTradeServiceImpl.class})
可以直接指定要測(cè)試的Service類(lèi),這樣Spring Boot就不會(huì)加載其他亂七八糟的依賴了,這樣會(huì)節(jié)約Unit Test運(yùn)行的時(shí)間。
寫(xiě)這篇文章最主要的目的還在于希望大家養(yǎng)成寫(xiě)Unit Test的好習(xí)慣,做一個(gè)注重代碼品質(zhì)的優(yōu)秀程序員!希望大家都能夠越變?cè)絻?yōu)秀,加油!
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國(guó)云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開(kāi)啟,新人活動(dòng)云服務(wù)器買(mǎi)多久送多久。
當(dāng)前名稱:聽(tīng)說(shuō)優(yōu)秀的程序員20%的時(shí)間都在寫(xiě)UT?-創(chuàng)新互聯(lián)
標(biāo)題URL:http://bm7419.com/article48/cdejep.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、網(wǎng)站改版、電子商務(wù)、云服務(wù)器、做網(wǎng)站、關(guān)鍵詞優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容