摘要:單元測試中是否要靜態方法,一直爭論不休,網上有一個一個又一個的討論,各種意見都有。真要用來靜態方法,一般都是結合使用。等工具不支持靜態方法,原理上是因為它們都是基于的,只能通過創建子類或實現接口的方式去。什么靜態方法構造函數,隨時隨地想就。
王者 Mockito
不知從何時開始,Mockito 成了 Java 的單元測試框架王者,目前(2019年7月)Github 上 star 數直逼 10K。看看其他的單元測試工具:PowerMock 2K(無疑是沾了 Mockito 的光),easymock 600,JMockit 300。跟 Mockito 一比,好可憐啊,一個能打的都沒有。
Mockito 當然很好。我從2012年還是2013年開始用 Mockito,看著它從 1.0x 版本一路走來,今年晚些時候估計會正式發布 3.0 版本。應該有不少人都跟我有類似的體驗,從 Mockito 開始接觸 mock / stub,一邊贊嘆 Mockito 語法的簡練,一邊享受著 mock 帶來的單元測試的便利性。總說單元測試應該要隔離外部依賴和實現,很難想象,如果沒有 mock,怎么寫單元測試呢?
public void test() { when(userDao.update(any(User.class))).thenReturn(1); int actual = userService.update(aUser); Assert.assertTrue(acutal > 0); verify(userDao).update(aUser); }
看看上面這個 Mockito 的例子,when(...).thenReturn(...),verify(...).doSomething(),這代碼就像人類語言,多么簡明易懂!
但是(沒錯轉折來了),已經2019年了,Mockito 依然不支持 mock 靜態方法、構造方法等。你可以說,這是設計理念,Mockito 首頁上一直寫著一句話 "Don’t mock everything" ,認為說應該做好功能代碼的設計,盡量避免靜態方法等,盡量使你的代碼易于測試。這個理念,在理論上沒問題,但這么多年的開發經驗告訴我,理想歸理想,實際上要你去維護的遺留代碼總是一籮筐一籮筐的,避無可避。
Static methods, to mock or not to mock, that is the question單元測試中是否要 mock 靜態方法,一直爭論不休,網上有 一個 一個 又一個 的討論,各種意見都有。
我的個人意見,跟 這個觀點 一樣,我認為測試工具不應該替用戶決定什么是好、什么是不好,而應該盡量提供選擇,讓用戶自行判斷、采取合適的方案。理論很美好,但實際情況就是,google 搜 "mockito how to mock static methods",有近15萬條結果,可想而知,全世界的開發者在這個問題上浪費了多少時間。
真要用 Mockito 來 mock 靜態方法,一般都是結合 PowerMock 使用。這兩年 PowerMock 發展的怎么樣我不太清楚,但14、15年那會兒我用過 PowerMock,感受就是,真他媽累啊!理論上來說是可以的,但實際做起來就總是各種問題,然后各種 google 、解決,然后又繼續各種問題,排查的我都快懷疑人生了。最終我是放棄了 PowerMock 的,這么費力地去結合兩個工具一起用,往后很難說還有多少坑。
Mockito、EasyMock 等工具不支持 mock 靜態方法,原理上是因為它們都是基于 cglib 的,只能通過創建子類或實現接口的方式去 mock。那除了 cglib ,就沒有其他的 mock 實現方法了嗎?當然有,修改字節碼呀!
另辟蹊徑的 JMockit和其他大多數使用 cglib 實現的單元測試工具不同,JMockit 使用 JDK6 的 java.lang.instrument 包和 ASM,動態地在運行時修改字節碼,從而實現 "Mock Anything" 。什么靜態方法、構造函數,隨時隨地想 mock 就 mock。一個 JMockit ,解決了 Mockito + PowerMock 兩個工具都解決不了的問題,那為啥不用 JMockit 呢?JMockit 為啥流行不起來呢?
public class UserServiceTest { @Tested private UserService userService; @Injectable private UserDao userDao; public void test() { new Expectations() { { userDao.update(withInstanceOf(User.class)); result = 1; } }; int actual = userService.update(aUser); Assert.assertTrue(acutal > 0); new Verifications() { { userDao.update(withInstanceOf(User.class)); } }; } }
功能更強大的 JMockit 卻流行不起來,我覺得其中一個原因,是它的語法不太友好。看看上面這個 JMockit 的例子,這坨 new Expectations(){...} 和 new Verifications(){...} 是什么鬼?匿名類?為啥里面又有一層大括號?別說測試代碼了,在普通的功能代碼中,我們都極少見到這樣的語法。多數人可能覺得不習慣,然后就此打住,放棄 JMockit 了。
JMockit 的這種語法,是基于它的 record-replay-verify 模型。new Expectations() 是錄制期望,new Verifications() 是校驗,二者中間的就是回放——正常調用業務方法。而在匿名內部類類中間的那層大括號,是 Java 的“實例初始化塊” (Instance Initialization Blocks),我們平時可能用“靜態初始化塊”比較多,“實例初始化塊”確實較少見,它的其中一種用途,就是用來初始化匿名內部類,因為匿名內部類不能有構造函數。理解了這些語法之后,其實 JMockit 不難懂,用法跟其他測試框架也大致一樣,就是功能更強大了。
JMockit 不夠流行的另一個原因,我猜可能跟社區有關。沒辦法,Mockito 太受歡迎了,社區一片火熱,貢獻者一大堆。反觀 JMockit,雖然開源,但只有原作者 Rogério Liesenfeld 自己一個人在開發維護。這種單人維護的項目,說不定哪一天就停更了,大家都會有這種擔憂。我也擔心啊,但看看近幾年 JMockit 的 release notes,基本上固定每一、兩個月一次發布,并且還會提前訂好下一次發布的計劃,真想對作者說一句:老哥,穩!所以,至少目前看來,JMockit 的穩定性、活躍性是不用擔心的,畢竟有個這么穩的作者。
JUnit5 + JMockit + Surefire + Jacoco 的配置例子想要安心用上 Junit5 和 JMockit,還想要單元測試覆蓋率?那還是有些坑要踩的。以 Maven 為例,有幾個留意點:
默認的 maven-surefire-plugin (運行單元測試的) 的版本不支持 JUnit5,得手動指定新版本號才行。
想用 jacoco-maven-plugin 得到單元測試覆蓋率的話,因為 jacoco 也用了修改字節碼的方案,默認配置下會和 JMockit 有沖突。需要一些額外配置才行,具體參考下面的例子。
完整的 Maven 配置例子:
4.0.0 io.github.renial java-utils 1.0.0 jar java-utils UTF-8 UTF-8 1.8 1.8 1.46 5.4.2 2.22.2 0.8.4 org.jmockit jmockit ${jmockit.version} test org.junit.jupiter junit-jupiter ${jupiter.version} test org.apache.maven.plugins maven-surefire-plugin ${surefire.version} -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar -javaagent:"${settings.localRepository}"/org/jacoco/org.jacoco.agent/${jacoco.version}/org.jacoco.agent-${jacoco.version}-runtime.jar=destfile=${project.build.directory}/jacoco.exec org.jacoco jacoco-maven-plugin ${jacoco.version} prepare-agent report test report
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75656.html
摘要:言歸正傳,上一篇文章單元測試如何開始介紹了幾款單元測試框架基本用法依賴隔離概念,本篇主要解答單元測試中幾個重要問題。在單元測試交流微信群,很多新進來的小伙伴,都會幾個大同小異的問題。 showImg(/img/bVEpaD?w=1080&h=715); 原文鏈接:http://www.jianshu.com/p/f5d197a4d83a 前言 已經一個月沒寫文章了,由于9月份在plan...
摘要:現在一般需要分前后端,因為大量前端框架和工具鏈的涌入根源是需求復雜了,讓前端可以跟后端獨立開來。但是,無論是前端去寫模板,亦或是完全前后端分離去寫,都脫離不了與后端進行數據交互。 showImg(https://segmentfault.com/img/remote/1460000007317424?w=350&h=113); --> GitHub地址 舊石器時代,Web 開發并不會去...
摘要:針對上面看到的問題,現在也有一些團隊在嘗試前后端之間加一個中間層比如淘寶的。淘寶有很多類似的文章,這里不贅述。淘寶團隊做了兩套接口文檔的維護工具,以及,不知道有沒有對外開放,兩個東西都是基于的一個嘗試,各有優劣。 原文出處: 小胡子哥的博客(@Barret李靖) 前后端分工協作是一個老生常談的大話題,很多公司都在嘗試用工程化的方式去提升前后端之間交流的效率,降低溝通成本,并且也開發了...
摘要:針對上面看到的問題,現在也有一些團隊在嘗試前后端之間加一個中間層比如淘寶的。淘寶有很多類似的文章,這里不贅述。淘寶團隊做了兩套接口文檔的維護工具,以及,不知道有沒有對外開放,兩個東西都是基于的一個嘗試,各有優劣。 原文出處: 小胡子哥的博客(@Barret李靖) 前后端分工協作是一個老生常談的大話題,很多公司都在嘗試用工程化的方式去提升前后端之間交流的效率,降低溝通成本,并且也開發了...
閱讀 2478·2021-09-22 16:05
閱讀 2961·2021-09-10 11:24
閱讀 3633·2019-08-30 12:47
閱讀 2941·2019-08-29 15:42
閱讀 3379·2019-08-29 15:32
閱讀 1946·2019-08-26 11:48
閱讀 1082·2019-08-23 14:40
閱讀 903·2019-08-23 14:33