【轉(zhuǎn)】關(guān)于java 單元測(cè)試Junit4和Mock的一些總結(jié)

原文出處請(qǐng)點(diǎn)擊這里

1. 單元測(cè)試的必要性

最近項(xiàng)目有在寫java代碼的單元測(cè)試娜睛,然后在思考一個(gè)問(wèn)題哆键,為什么要寫單元測(cè)試掘托??單元測(cè)試寫了有什么用籍嘹?烫映?百度了一圈,如下:

  • 軟件質(zhì)量最簡(jiǎn)單噩峦、最有效的保證;
  • 是目標(biāo)代碼最清晰抽兆、最有效的文檔识补;
  • 可以優(yōu)化目標(biāo)代碼的設(shè)計(jì);
  • 是代碼重構(gòu)的保障辫红;
  • 是回歸測(cè)試和持續(xù)集成的基石凭涂。

由于開發(fā)經(jīng)驗(yàn)有限,可能說(shuō)的不太對(duì)贴妻,但是是我目前的個(gè)人的觀點(diǎn)切油,寫單元測(cè)試,有時(shí)候確實(shí)可以發(fā)現(xiàn)bug名惩,+ 但是發(fā)現(xiàn)bug次數(shù)很少澎胡,而且目前都是項(xiàng)目開發(fā)完了,要上線了娩鹉,公司有80%的覆蓋率要求攻谁,所以都是后期上線之前補(bǔ)。目前而言弯予,并沒(méi)有在很認(rèn)真地寫UT戚宦,只是想著完成上線要求。這個(gè)東西吧锈嫩,也是看成本要求受楼,如果一個(gè)新項(xiàng)目要緊急上線垦搬,走緊急發(fā)布特殊流程,單元測(cè)試后期時(shí)間充裕了再補(bǔ)上也行艳汽。所以猴贰,在時(shí)間允許情況下,我覺(jué)得還是要寫UT骚灸,做了有時(shí)候確實(shí)能發(fā)現(xiàn)一些問(wèn)題糟趾,尤其對(duì)于一個(gè)大的項(xiàng)目來(lái)說(shuō),一個(gè)bug被隱藏的時(shí)間越長(zhǎng)甚牲,修復(fù)這個(gè)bug的代價(jià)就越大义郑。在《快速軟件開發(fā)》一書中已引用了大量的研究數(shù)據(jù)指出:最后才修改一個(gè) bug 的代價(jià)是在bug產(chǎn)生時(shí)修改它的代價(jià)的10倍。此外丈钙,還能學(xué)到一些單元測(cè)試的知識(shí)非驮,也算是一種技能上的進(jìn)步吧。

2. Junit4 與 Mock 的介紹

目前應(yīng)用比較普遍的java單元測(cè)試工具 junit4+Mock(Mockito /jmock / powermock)或Stub(用得較少雏赦,一般不推薦)劫笙,由于junit3目前用得不多,基本升級(jí)到j(luò)unit4了星岗,所以就直接簡(jiǎn)單說(shuō)下junit4填大。

問(wèn)題一:為什么需要mock或stub?它與junit什么關(guān)系俏橘?

在做單元測(cè)試的時(shí)候允华,我們會(huì)發(fā)現(xiàn)我們要測(cè)試的方法會(huì)引用很多外部依賴的對(duì)象,比如:(發(fā)送郵件寥掐,網(wǎng)絡(luò)通訊靴寂,記錄Log, 文件系統(tǒng) 之類的)。 而我們沒(méi)法控制這些外部依賴的對(duì)象召耘。 為了解決這個(gè)問(wèn)題百炬,我們需要用到Stub和Mock來(lái)模擬這些外部依賴的對(duì)象,從而控制它們。

JUnit是單元測(cè)試框架污它,可以輕松的完成關(guān)聯(lián)依賴關(guān)系少或者比較簡(jiǎn)單的類的單元測(cè)試剖踊,但是對(duì)于關(guān)聯(lián)到其它比較復(fù)雜的類或?qū)\(yùn)行環(huán)境有要求的類的單元測(cè)試,模擬環(huán)境或者配置環(huán)境會(huì)非常耗時(shí)衫贬,實(shí)施單元測(cè)試比較困難蜜宪。而這些“mock框架”(Mockito 、jmock 祥山、 powermock圃验、EasyMock),可以通過(guò)mock框架模擬一個(gè)對(duì)象的行為缝呕,從而隔離開我們不關(guān)心的其他對(duì)象澳窑,使得測(cè)試變得簡(jiǎn)單斧散。(例如service調(diào)用dao,即service依賴dao摊聋,我們可以通過(guò)mock dao來(lái)模擬真實(shí)的dao調(diào)用鸡捐,從而能達(dá)到測(cè)試service的目的。)

模擬對(duì)象(Mock Object)可以取代真實(shí)對(duì)象的位置麻裁,用于測(cè)試一些與真實(shí)對(duì)象進(jìn)行交互或依賴于真實(shí)對(duì)象的功能箍镜,模擬對(duì)象的背后目的就是創(chuàng)建一個(gè)輕量級(jí)的、可控制的對(duì)象來(lái)代替測(cè)試中需要的真實(shí)對(duì)象煎源,模擬真實(shí)對(duì)象的行為和功能色迂。

問(wèn)題二:mock與stub什么區(qū)別?

Mock和Stub是兩種測(cè)試代碼功能的方法手销。Mock測(cè)重于對(duì)功能的模擬歇僧,Stub測(cè)重于對(duì)功能的測(cè)試重現(xiàn)。比如對(duì)于List接口锋拖,Mock會(huì)直接對(duì)List進(jìn)行模擬诈悍,而Stub會(huì)新建一個(gè)實(shí)現(xiàn)了List的TestList,在其中編寫測(cè)試的代碼兽埃。
強(qiáng)烈建議優(yōu)先選擇Mock方式侥钳,因?yàn)镸ock方式下,模擬代碼與測(cè)試代碼放在一起柄错,易讀性好舷夺,而且擴(kuò)展性、靈活性都比Stub好鄙陡。

其中EasyMock和Mockito對(duì)于Java接口使用接口代理的方式來(lái)模擬,對(duì)于Java類使用繼承的方式來(lái)模擬(也即會(huì)創(chuàng)建一個(gè)新的Class類)躏啰。Mockito支持spy方式趁矾,可以對(duì)實(shí)例進(jìn)行模擬。但它們都不能對(duì)靜態(tài)方法和final類進(jìn)行模擬给僵,powermock通過(guò)修改字節(jié)碼來(lái)支持了此功能毫捣。

有篇文章介紹:http://blog.csdn.net/devhubs/article/details/8018084

二、junit4相關(guān)介紹

這里有篇文章介紹了junit4的一些帝际,包括怎么引入蔓同,使用,蠻詳細(xì)蹲诀。---》 http://blog.csdn.net/happylee6688/article/details/38069761

這邊就記錄一些常用注解斑粱,當(dāng)做學(xué)習(xí)方便。

常用注解
@Before:初始化方法脯爪,在任何一個(gè)測(cè)試方法執(zhí)行之前则北,必須執(zhí)行的代碼矿微。對(duì)比 JUnit 3 ,和 setUp()方法具有相同的功能尚揣。在該注解的方法中涌矢,可以進(jìn)行一些準(zhǔn)備工作,比如初始化對(duì)象快骗,打開網(wǎng)絡(luò)連接等娜庇。

@After:釋放資源,在任何一個(gè)測(cè)試方法執(zhí)行之后方篮,需要進(jìn)行的收尾工作名秀。對(duì)比 JUnit 3 ,和 tearDown()方法具有相同的功能恭取。

@Test:測(cè)試方法泰偿,表明這是一個(gè)測(cè)試方法。在 JUnit 中將會(huì)自動(dòng)被執(zhí)行蜈垮。對(duì)與方法的聲明也有如下要求:名字可以隨便取耗跛,沒(méi)有任何限制,但是返回值必須為 void 攒发,而且不能有任何參數(shù)调塌。如果違反這些規(guī)定,會(huì)在運(yùn)行時(shí)拋出一個(gè)異常惠猿。不過(guò)羔砾,為了培養(yǎng)一個(gè)好的編程習(xí)慣,我們一般在測(cè)試的方法名上加 test 偶妖,比如:testAdd()姜凄。
同時(shí),該 Annotation(@Test) 還可以測(cè)試期望異常和超時(shí)時(shí)間趾访,如 @Test(timeout=100)态秧,我們給測(cè)試函數(shù)設(shè)定一個(gè)執(zhí)行時(shí)間,超過(guò)這個(gè)時(shí)間(100毫秒)扼鞋,他們就會(huì)被系統(tǒng)強(qiáng)行終止申鱼,并且系統(tǒng)還會(huì)向你匯報(bào)該函數(shù)結(jié)束的原因是因?yàn)槌瑫r(shí),這樣你就可以發(fā)現(xiàn)這些 bug 了云头。而且捐友,它還可以測(cè)試期望的異常,例如溃槐,我們剛剛的那個(gè)空指針異常就可以這樣:@Test(expected=NullPointerException.class)匣砖。

@Ignore:忽略的測(cè)試方法,標(biāo)注的含義就是“某些方法尚未完成,咱不參與此次測(cè)試”脆粥;這樣的話測(cè)試結(jié)果就會(huì)提示你有幾個(gè)測(cè)試被忽略砌溺,而不是失敗。一旦你完成了相應(yīng)的函數(shù)变隔,只需要把 @Ignore 注解刪除即可规伐,就可以進(jìn)行正常測(cè)試了。當(dāng)然匣缘,這個(gè) @Ignore 注解對(duì)于像我這樣有“強(qiáng)迫癥”的人還是大有意義的猖闪。每當(dāng)看到紅色條(測(cè)試失敗)的時(shí)候就會(huì)全身不舒服肌厨,感覺(jué)無(wú)法忍受(除非要測(cè)試的目的就是讓它失斉嗷拧)。當(dāng)然柑爸,對(duì)代碼也是一樣吵护,無(wú)法忍受那些雜亂不堪的代碼。

@BeforeClass:針對(duì)所有測(cè)試表鳍,也就是整個(gè)測(cè)試類中馅而,在所有測(cè)試方法執(zhí)行前,都會(huì)先執(zhí)行由它注解的方法譬圣,而且只執(zhí)行一次瓮恭。當(dāng)然,需要注意的是厘熟,修飾符必須是 public static void xxxx 屯蹦;此 Annotation 是 JUnit 4 新增的功能。

@AfterClass:針對(duì)所有測(cè)試绳姨,也就是整個(gè)測(cè)試類中登澜,在所有測(cè)試方法都執(zhí)行完之后,才會(huì)執(zhí)行由它注解的方法飘庄,而且只執(zhí)行一次脑蠕。當(dāng)然,需要注意的是竭宰,修飾符也必須是 public static void xxxx 空郊;此 Annotation 也是 JUnit 4 新增的功能份招,與 @BeforeClass 是一對(duì)切揭。

執(zhí)行順序
所以,在 JUnit 4 中锁摔,單元測(cè)試用例的執(zhí)行順序?yàn)椋?/p>

三廓旬、Mock的幾種比較(Mockito 、jmock 、 powermock)

介紹文章一:http://blog.csdn.net/luvinahlc/article/details/10442743

介紹文章二:http://blog.csdn.net/zhangxin09/article/details/42422643

介紹文章三(Mockito 文檔):https://static.javadoc.io/org.mockito/mockito-core/2.8.47/org/mockito/Mockito.html

Spring提供了對(duì)Junit支持孕豹,可以使用注解的方式(注解加在需要測(cè)試的類上):

@RunWIth(SpringJunit4ClassRunner.class) ---->為了讓測(cè)試在Spring容器環(huán)境下執(zhí)行

@ContextConfiguration(locations = {"classpath:applicationContext.xml"} --->用來(lái)指明Spring的配置文件位置

Mockito簡(jiǎn)單運(yùn)用說(shuō)明

① when(mock.someMethod()).thenReturn(value):設(shè)定mock對(duì)象某個(gè)方法調(diào)用時(shí)的返回值涩盾。可以連續(xù)設(shè)定返回值励背,即when(mock.someMethod()).thenReturn(value1).then
Return(value2),第一次調(diào)用時(shí)返回value1,第二次返回value2春霍。也可以表示為如下:
when(mock.someMethod()).thenReturn(value1,value2)叶眉。
② 調(diào)用以上方法時(shí)拋出異常: when(mock.someMethod()).thenThrow(new Runtime
Exception());
③ 另一種stubbing語(yǔ)法:
doReturn(value).when(mock.someMethod())
doThrow(new RuntimeException()).when(mock.someMethod())
④ 對(duì)void方法進(jìn)行方法預(yù)期設(shè)定只能用如下語(yǔ)法:
doNothing().when(mock.someMethod())
doThrow(new RuntimeException()).when(mock.someMethod())
doNothing().doThrow(new RuntimeException()).when(mock.someMethod())
⑤ 方法的參數(shù)可以使用參數(shù)模擬器址儒,可以將anyInt()傳入任何參數(shù)為int的方法,即anyInt匹配任何int類型的參數(shù)衅疙,anyString()匹配任何字符串莲趣,anySet()匹配任何Set。
⑥ Mock對(duì)象只能調(diào)用stubbed方法饱溢,調(diào)用不了它真實(shí)的方法喧伞,但是Mockito可以用spy來(lái)監(jiān)控一個(gè)真實(shí)對(duì)象,這樣既可以stubbing這個(gè)對(duì)象的方法讓它返回我們的期望值绩郎,又可以使得對(duì)其他方法調(diào)用時(shí)將會(huì)調(diào)用它的真實(shí)方法潘鲫。
⑦ Mockito會(huì)自動(dòng)記錄自己的交互行為,可以用verify(…).methodXxx(…)語(yǔ)法來(lái)驗(yàn)證方法Xxx是否按照預(yù)期進(jìn)行了調(diào)用嗽上。
(1) 驗(yàn)證調(diào)用次數(shù):verify(mock,times(n)).someMethod(argument),n為被調(diào)用的次數(shù)次舌,如果超過(guò)或少于n都算失敗。除了times(n)兽愤,還有never(),atLease(n),atMost(n)彼念。
(2) 驗(yàn)證超時(shí):verify(mock, timeout(100)).someMethod();
(3) 同時(shí)驗(yàn)證:verify(mock, timeout(100).times(1)).someMethod();

相關(guān)注解:

MockitoAnnotations.initMocks(this);

initializes fields annotated with Mockito annotations.

Allows shorthand creation of objects required for testing.
Minimizes repetitive mock creation code.
Makes the test class more readable.
Makes the verification error easier to read because field name is used to identify the mock.

ReflectionTestUtils.setField(AopTargetUtils.getTarget(appInfoService), "openAppInfoMapper",openAppInfoMapperMock);

但是由于Spring可以使用@Autoware類似的注解方式,對(duì)私有的成員進(jìn)行賦值浅萧,此時(shí)無(wú)法直接對(duì)私有的依賴設(shè)置mock對(duì)象逐沙。可以通過(guò)引入ReflectionTestUtils洼畅,解決依賴注入的問(wèn)題吩案。

(不是很理解。帝簇。徘郭。。丧肴,因?yàn)槲覍?duì)某個(gè)service的private dao残揉,直接mock,并沒(méi)有設(shè)置ReflectionTestUtils.setField()芋浮,照樣可以運(yùn)行ok抱环,那么這個(gè)什么時(shí)候用到?。)

@InjectMocks --- injects mock or spy fields into tested object automatically.

這個(gè)注解不會(huì)把一個(gè)類變成mock或是spy镇草,但是會(huì)把當(dāng)前對(duì)象下面的Mock/Spy類注入進(jìn)去,按類型注入眶痰。

@Mock 生成的類,所有方法都不是真實(shí)的方法梯啤,而且返回值都是NULL竖伯。---> when(dao.getOrder()).thenReturn("returened by mock ");

@Spy ---Creates a spy of the real object. The spy calls real methods unless they are stubbed.

生成的類,所有方法都是真實(shí)方法因宇,返回值都是和真實(shí)方法一樣的黔夭。---> doReturn("twotwo").when(ps).getPriceTwo();

Mockito可以完成對(duì)一般對(duì)象方法的模擬,但是對(duì)于靜態(tài)函數(shù)羽嫡、構(gòu)造函數(shù)本姥、私有函數(shù)等還是無(wú)能為力.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市杭棵,隨后出現(xiàn)的幾起案子婚惫,更是在濱河造成了極大的恐慌,老刑警劉巖魂爪,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件先舷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡滓侍,警方通過(guò)查閱死者的電腦和手機(jī)蒋川,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)撩笆,“玉大人捺球,你說(shuō)我怎么就攤上這事∠Τ澹” “怎么了氮兵?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)歹鱼。 經(jīng)常有香客問(wèn)我泣栈,道長(zhǎng),這世上最難降的妖魔是什么弥姻? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任南片,我火速辦了婚禮,結(jié)果婚禮上庭敦,老公的妹妹穿的比我還像新娘疼进。我一直安慰自己,他們只是感情好螺捐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布颠悬。 她就那樣靜靜地躺著,像睡著了一般定血。 火紅的嫁衣襯著肌膚如雪赔癌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天澜沟,我揣著相機(jī)與錄音灾票,去河邊找鬼。 笑死茫虽,一個(gè)胖子當(dāng)著我的面吹牛刊苍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播濒析,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼正什,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了号杏?” 一聲冷哼從身側(cè)響起婴氮,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盾致,沒(méi)想到半個(gè)月后主经,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡庭惜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年罩驻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片护赊。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡惠遏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骏啰,到底是詐尸還是另有隱情爽哎,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布器一,位于F島的核電站课锌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏祈秕。R本人自食惡果不足惜渺贤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望请毛。 院中可真熱鬧志鞍,春花似錦、人聲如沸方仿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至此洲,卻和暖如春厂汗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呜师。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工娶桦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汁汗。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓衷畦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親知牌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祈争,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容