dubbo应用程序的单元测试环境搭建(springtest,powermock,mockito)
轉:http://blog.csdn.net/yys79/article/details/66472797
最近,項目中頻繁用到dubbo,而且java工程用引用了幾十個關聯系統的服務(如用戶認證,基礎服務,客戶平臺)。這些服務都是dubbo服務,對我們僅提供了一個接口,服務通過zookeeper注冊,并給我們提供服務。我們的項目都是基于spring的。spring集成dubbo,就可以對這些外部服務進行注入和使用了。
? ? 但是對于單元測試來說卻出現了難題:領域模型的測試不是問題,主要都是自己的代碼,加上一些mock就可以輕松測試;但是如果我想測試應用服務層(使用外部服務最多的地方),很多情況下就需要啟動spring環境,而這樣就需要加載外部系統的服務了。問題是外部的服務給我們的jar包中,只有服務的接口。啟動時如果按照正常開發環境的配置加載spring context,那么明顯是依賴了外部環境,如果沒有啟動zookeeper或者本機不聯網,抑或是關聯系統沒有啟動,spring context加載將會失敗,這是單元測試的忌諱。如果使用專門的單元測試的spring配置文件,去掉外部關聯系統的consumer配置,啟動會直接失敗,更別提測試了。
?還有寫其他問題,如測試靜態方法,私有方法;mock框架與springtest如何集成。spring的aop代理類如何mock一些默認的實現,測試數據庫如何選擇。總之問題超多。好吧,該進入正題了。
? ?1.測試靜態類,私有方法的問題
? ? ? ? 簡單一句話,用powermock。powermock可以做到修改字節碼而改變類的行為,這不多說了,大家自己搜一下,官網上例子通俗易懂。目前我在maven中的關于powermock,mockito的依賴是這樣加入的:
? ?
[html]?view plain?copy?
最后的話這個jacoco不是mock的依賴,是一個測試覆蓋率的插件。也推薦一下給大家用,哈哈。
?
? ? 2.powermock與springtest配合使用的問題
? ? ? ? 第一個問題解決了,不錯!第二個問題就來了。spring標準的Runner是SpringJUnit4ClassRunner,如果用這個Runner,那么powermock的@PrepairForTest就沒法使用了(也就是靜態mock,私有方法mock的關鍵),因此如果想使用靜態和私有方法mock就必須使用用Powemock的Runner,但是又如何啟動spring context呢?
? ? ?經過一些查找,終于解決了這個問題,方法就是用powermock的代理, 在測試類上加上這樣的注解:
? ? ?
[java]?view plain?copy?
?
Runner使用PowerMockRuner(就是RunWith注解的值);使用powermock提供的代理來使用SpringJUnit4ClassRunner;@PowerMockIgnore的作用是忽略一些powermock使用的classloader無法處理的類,不使用的話,啟動用例就會報錯。
?
[java]?view plain?copy?
? 到此,一個基于PowerMock,springtest和Mockito的基本配置就都弄完了。
上一篇說到powermock的配置,我一般在測試類中再加上繼承spring的測試類:extends AbstractTransactionalJUnit4SpringContextTests ,這樣就基本可以了。
再來說說上一篇中使用的spring配置文件。主要的不同就是test-spring.xml里面不會包含哪些引用外部服務的consumer,也就是剔除外部dubbo服務。
但是代碼里有很多注入外部服務的地方,這如何處理呢?這是第三個問題:
? 3.注入外部的服務:
? ? ? 開始我想了個很笨的方法:在test/文件夾下給外部服務的接口都提供一個空的實現類(implements 接口,然后用eclpse生成默認的方法實現)。這樣基本上就可以啟動了。但是實際使用中,由于外部服務接口也在不斷修改中,會出現不同環境的接口類不一至的情況。比如uat環境的jar包多了或一個方法(雖然我們的程序沒有直接使用),如此一來,我自己搞的空實現類就會報編譯錯誤了。
? ?后來想到了一個方法,在/test的代碼中增加一個普通的@Conponent注解的類,類里面使用@Bean注解標明所有外部類的生成方法
?
[java]?view plain?copy?
? 然后在測試類中注入這個MockedOuterBeanFactory,這樣測試環境的spring就可以完整的啟動了。外部的服務在啟動后都是Mocktio生成的代理類,所有方法都會返回默認值。
在實際測試中如何打樁呢?也很簡單。
? ? ? ? ? ?如果我測試一個自己寫的服務(如MyService),MyService又注入了OuterService(外部服務),那么利用spring Bean注入的單例這個特性就可以完成。在MyService的測試類中(MyServiceTest.java),同樣也注入OuterService,在執行MyService的方法之前對OuterService進行打樁。那么由于bean是單例的,MyServiceTest中注入的OuterService實例就是MyService注入的實例。這樣就輕松完成了打樁的工作。如果有特殊原因,main中配置的bean不是單例的,那么可以的話,在test-spring.xml中把它配置為單例的就可以。如果確實情況特殊不允許配置為單例方式,看下一篇吧。
?啟動后
?解決了spring啟動的問題,然后呢?數據庫
? 4.測試數據庫的選擇
? 有時候,我們需要測試持久化的內容,比如分頁查詢,不能說測試覆蓋了代碼就可以,還需要驗證查詢到的數據是否符合要求。參考了dbunits之類的東西,最后還是覺得之前使用的h2database是最好的選擇。它可以使用內存模式,不需要外部數據庫的依賴。這樣單元測試才能獨立運行。配置很簡單,
首先加入依賴:
?
[html]?view plain?copy至于版本,就自己找個最新的吧。
?
然后在數據源的地方使用如下配置(這也是測試環境spring配置不同于main配置的主要位置):
?
[html]?view plain?copy
注意:MODE=MySQL,這是讓h2模擬mysql庫,如果你使用其他類型的庫,一般也會有對應的Mode,主流數據庫都支持。注意mem項,意思是內存數據庫,這樣配置根本不會生成數據庫文件的,特別適合單元測試(依賴外部環境就不是標準單元測試了)。至于數據源類型,按自己的工程的配置就好,只要使用h2的url和driver就行,這里用的是tomcat數據源。
?
這些配置都做好后,就可以運行真正的powermock,mockito,springtest的單元測試了。下一篇說說怎么測試aop的類。
?
上兩篇中,基本環境和測試方式都說了一下。基本的測試否沒問題了。但是還有些問題需要解決。在我實際的開發中,最主要是是要做有Aop切面的Bean內部注入的bean打樁。
基本情況是:
?MyService是個接口,其實現類MyServiceImpl是@Transactional注解的Bean(這樣注入的MyService實例實際上就是代理了)
MyServiceImpl注了一個Bean:InnerBean,innerBean是自己工程中實現或其他服務都無所謂
測試中想使用mock替換這個InnerBean。
?
在spring中,aop用代理實現的。PowerMock不能修改其字節碼。而在測試中,我需要替換MyService代理中的InnerBean實例。開始傷透了腦筋啊。。。
如果不能打樁,那么必須老老實實的準備fixture才能測試,比如準備數據庫中多個表的數據,才能保證InnerBean完成我的預期結果(這種情況還算好的,有些情況都不能打樁)。
?
這個其實真是不難,只不過之前不太熟悉spring的測試框架(以前拋棄了spring,所以也不怎么研究)。
springtest有2個Utils類,可以幫助我們拿到MyService代理中的具體實現類:
?
[java]?view plain?copy?
[java]?view plain?copy
?
這樣就可以拿到具體實現類了,再加一句impl.innerBean = mockInnerBean;就可以用自己打樁過的mock替換注入的innerBean實例了。如果多于一個測試方法,別忘了finally時候替換回來啊。
impl.innerBean 這里,我一般的注入bean都是是用package級別的,這樣便于測試,不必特別的依賴其他技術就可以替換實現。如果是private的,那么用ReflectionTestUtils吧,具體不用說了,簡單易用。
總結
以上是生活随笔為你收集整理的dubbo应用程序的单元测试环境搭建(springtest,powermock,mockito)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一步步开始集中管理[为企业部署Windo
- 下一篇: 单点登录总结(域名内与跨域名)