摘要:對(duì)于依賴注入,以下簡稱的準(zhǔn)確定義可以在這里找到。在單元測試?yán)锩娴膽?yīng)用所謂在單元測試?yán)锩娴膽?yīng)用,其實(shí)說白了就是使用模式,將出來的到里面去。最后,如果你也對(duì)安卓單元測試感興趣的話,歡迎加入我們的交流群作者小創(chuàng)更多文章公眾號(hào)
在上一篇文章中,我們講了要將mock出來的dependency真正使用起來,需要在測試環(huán)境下通過某種方式set 到用到它的那個(gè)對(duì)象里面進(jìn)去,替換掉真實(shí)的實(shí)現(xiàn)。我們前面舉的例子是:
public class LoginPresenter { private UserManager mUserManager = new UserManager(); public void login(String username, String password) { //。。。some other code mUserManager.performLogin(username, password); } }
在測試LoginPresenter#login()時(shí),為了能夠?qū)ock出來的UserManager set到LoginPresenter里面,我們前面的做法是簡單粗暴,給LoginPresenter加一個(gè)UserManager的setter。然而這種做法畢竟不是很優(yōu)雅,一般來說,我們正式代碼里面是不會(huì)去調(diào)用這個(gè)setter,修改UserManager這個(gè)對(duì)象的。因此這個(gè)setter存在的意義就純粹是為了方便測試。這個(gè)雖然不是沒有必要,卻不是太好看,因此在有選擇的情況下,我們不這么做。在這里,我們介紹依賴注入這種模式。
對(duì)于依賴注入(Dependency Injection,以下簡稱DI)的準(zhǔn)確定義可以在這里找到。它的基本理念這邊簡單描述下,首先這是一種代碼模式,這個(gè)模式里面有兩個(gè)概念:Client和Dependency。假如你的代碼里面,一個(gè)類用到了另外一個(gè)類,那么前者叫Client,后者叫Dependency。結(jié)合上面的例子,LoginPresenter用到了UserManager,那么LoginPresenter叫Client,UserManager叫Dependency。當(dāng)然,這是個(gè)相對(duì)的概念,一個(gè)類可以是某個(gè)類的Dependency,卻是另外一個(gè)類的Client。比如說如果UserManager里面用到了Retrofit,那么相對(duì)于Retrofit,UserManager又是Dependency。DI的基本思想就是,對(duì)于Dependency的創(chuàng)建過程,并不在Client里面進(jìn)行,而是由外部創(chuàng)建好,然后通過某種方式set到Client里面。這種模式,就叫做依賴注入。
是的,依賴注入就是這么簡單的一個(gè)概念,這邊需要澄清的一點(diǎn)是,這個(gè)概念本身跟dagger2啊,RoboGuice這些框架并沒有什么關(guān)系。現(xiàn)在很多介紹DI的文章往往跟dagger2是在一起的,因?yàn)閐agger2的使用相對(duì)來說不是很直觀,所以導(dǎo)致很多人認(rèn)為DI是多么復(fù)雜的東西,甚至認(rèn)為只能用dagger等框架來實(shí)現(xiàn)依賴注入,其實(shí)不是這樣的。實(shí)現(xiàn)依賴注入很簡單,dagger這些框架只是讓這種實(shí)現(xiàn)變得更加簡單,簡潔,優(yōu)雅而已。
DI的常見實(shí)現(xiàn)方式下面介紹DI的實(shí)現(xiàn)方式,通常來說,這里是大力介紹dagger2的地方。但是,雖然dagger2的確是非常好的東西,然而如果我直接介紹dagger2的話,會(huì)很容易導(dǎo)致一個(gè)誤區(qū),認(rèn)為在測試的時(shí)候,也只能用dagger來做依賴注入或創(chuàng)建對(duì)應(yīng)的測試類,因此,我這邊刻意不介紹dagger。先讓大家知道最基本的DI怎么實(shí)現(xiàn),然后在測試的時(shí)候如何更方便高效的使用。
實(shí)現(xiàn)DI這種模式其實(shí)很簡單,有多種方式,上一篇文章中提到的setter,其實(shí)就是實(shí)現(xiàn)DI的一種方式,叫做 setter injection 。此外,通過方法的參數(shù)傳遞進(jìn)去(argument injection),也是實(shí)現(xiàn)DI的一種方式:
public class LoginPresenter { //這里,LoginPresenter不再持有UserManager的一個(gè)引用,而是作為方法參數(shù)直接傳進(jìn)去 public void login(UserManager userManager, String username, String password) { //... some other code userManager.performLogin(username, password); } }
然而更常用的方式,是將Dependency作為Client的構(gòu)造方法的參數(shù)傳遞進(jìn)去:
public class LoginPresenter { private final UserManager mUserManager; //將UserManager作為構(gòu)造方法參數(shù)傳進(jìn)來 public LoginPresenter(UserManager userManager) { this.mUserManager = userManager; } public void login(String username, String password) { //... some other code mUserManager.performLogin(username, password); } }
這種實(shí)現(xiàn)DI的模式叫 Constructor Injection。其實(shí)一般來說,提到DI,指的都是這種方式。這種方式的好處是,依賴關(guān)系非常明顯。你必須在創(chuàng)建這個(gè)類的時(shí)候,就提供必要的dependency。這從某種程度上來說,也是在說明這個(gè)類所完成的功能。因此,盡量使用 Constructor injection。
說到這里,你可能會(huì)有一個(gè)疑問,如果把依賴都聲明在Constructor的參數(shù)里面,這會(huì)不會(huì)讓這個(gè)類的Constructor參數(shù)變得非常多?如果真的發(fā)生這種情況了,那往往說明這個(gè)類的設(shè)計(jì)是有問題的,需要重構(gòu)。為什么呢?我們代碼里面的類,一般可以分為兩種,一種是Data類,比如說UserInfo,OrderInfo等等。另外一種是Service類,比如UserManager, AudioPlayer等等。所以這個(gè)問題就有兩種情況了:
如果Constructor里面?zhèn)魅氲暮芏嗍腔绢愋偷臄?shù)據(jù)或數(shù)據(jù)類,那么或許你要做的,是創(chuàng)建一個(gè)(或者是另一個(gè))數(shù)據(jù)類把這些數(shù)據(jù)封裝一下,這個(gè)過程的價(jià)值可是大大滴!而不僅僅是封裝一下參數(shù)的問題,有了一個(gè)類,很多的方法就可以放到這個(gè)類里面了。這點(diǎn)請(qǐng)參考Martin Fowler的《重構(gòu)》第十章“Introduce Parameter Object”。
如果傳入的很多是service類,那么這說明這個(gè)類做的事情太多了,不符合單一職責(zé)的原則(Single Responsibility Principle,SRP),因此,需要重構(gòu)。
接下來說回我們的初衷:DI在測試?yán)锩娴膽?yīng)用。
DI在單元測試?yán)锩娴膽?yīng)用所謂DI在單元測試?yán)锩娴膽?yīng)用,其實(shí)說白了就是使用DI模式,將mock出來的Dependency set到Client里面去。我相信這篇文章解釋到這里,那么答案也就比較明顯了,為了強(qiáng)調(diào)我們要盡量使用 Constructor injection,對(duì)于 setter Injection 和 Argument injection 這邊就不做代碼示例了。
如果你的代碼使用的是 Constructor injection:
public class LoginPresenter { private final UserManager mUserManager; //將UserManager作為構(gòu)造方法參數(shù)傳進(jìn)來 public LoginPresenter(UserManager userManager) { this.mUserManager = userManager; } public void login(String username, String password) { //... some other code mUserManager.performLogin(username, password); } }
其中我們要測的方法是login(), 要驗(yàn)證login()方法調(diào)用了mUserManager的performLigon()。對(duì)應(yīng)的測試方法如下:
public class LoginPresenterTest { @Test public void testLogin() { UserManager mockUserManager = Mockito.mock(UserManager.class); LoginPresenter presenter = new LoginPresenter(mockUserManager); //創(chuàng)建的時(shí)候,講mock傳進(jìn)去 presenter.login("xiaochuang", "xiaochuang password"); Mockito.verify(mockUserManager).performLogin("xiaochuang", "xiaochuang password"); } }
很簡單,對(duì)吧。
小結(jié)這篇文章介紹了DI的概念,以及在單元測試?yán)锩娴膽?yīng)用,這里特意沒有介紹dagger2的使用,目的是要強(qiáng)調(diào):
一個(gè)靈活的,易于測試的,符合SRP的,結(jié)構(gòu)清晰的項(xiàng)目,關(guān)鍵在于要應(yīng)用依賴注入這種模式,而不是用什么來做依賴注入。
等你學(xué)會(huì)使用dagger以后,要記得在測試的時(shí)候,如果可以直接mock dependency并傳給被測類,那就直接創(chuàng)建,不是一定要使用dagger來做DI
然而如果完全不使用框架來做DI,那么在正式代碼里面就有一個(gè)問題了,那就是dependency的創(chuàng)建工作就交給上層client去處理了,這可不是件好事情。想想看,LoginActivity里面創(chuàng)建LoginPresenter的時(shí)候,還得知道LoginPresenter用了UserManager。然后創(chuàng)建一個(gè)UserManager對(duì)象給LoginPresenter。對(duì)于LoginActivity來說,它覺得我才懶得管你用什么樣的UserManager呢,我只想告訴你login的時(shí)候,你給我老老實(shí)實(shí)的login就好了,你用什么Manager我不管。所以,直接在LoginActivity里面創(chuàng)建UserManager,可能不是個(gè)好的選擇。那怎么樣算是一個(gè)好的選擇呢?dagger2給了我們答案。
于是下一篇文章我們介紹dagger2。
文中的代碼在github這個(gè)項(xiàng)目里面。
最后,如果你也對(duì)安卓單元測試感興趣的話,歡迎加入我們的交流群:
作者 小創(chuàng) 更多文章 | Github | 公眾號(hào)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/65896.html
摘要:前言越來越多的項(xiàng)目開始嘗試寫單元測試,關(guān)于單元測試的好處以及原理已經(jīng)有很多資料了,這里不在做過多的講述,本文主要介紹單元測試在模塊化應(yīng)用中的一些思考,以及如何優(yōu)雅的寫單元測試。最后,依賴注入來寫單元測試。 本文由作者潘威授權(quán)網(wǎng)易云社區(qū)發(fā)布。 前言越來越多的項(xiàng)目開始嘗試寫單元測試,關(guān)于單元測試的好處以及原理已經(jīng)有很多資料了,這里不在做過多的講述,本文主要介紹單元測試在模塊化應(yīng)用中的一些思...
摘要:輸出結(jié)果需要人工檢查的測試不是一個(gè)好的單元測試。為了有效的進(jìn)行單元測試,需要遵循一定的方法,通常采用路徑覆蓋法設(shè)計(jì)單元測試用例。 在微服務(wù)架構(gòu)下高覆蓋率的單元測試是保障代碼質(zhì)量的第一道也是最重要的關(guān)口,應(yīng)該持之以恒。 背景 單元測試為代碼質(zhì)量保駕護(hù)航,是提高業(yè)務(wù)質(zhì)量的最直接手段,實(shí)踐證明,非常多的缺陷完全可以通過單元測試來發(fā)現(xiàn),測試金字塔提出者M(jìn)artin Fowler 強(qiáng)調(diào)如果一個(gè)高...
摘要:同時(shí)增加了單元測試,使用了,增加了可視化配置權(quán)限,增加了自定義布局等等,優(yōu)化了原先的權(quán)限方案,支持不刷新頁面更新路由等等功能。雖然它的初衷是為了單元測試的,但正好滿足了我們的需求。它會(huì)重寫瀏覽器的對(duì)象,從而才能攔截所有請(qǐng)求,代理到本地。 前言 vue-element-admin 從 2017.04.17提交第一個(gè) commit 以來,維護(hù)至今已經(jīng)有兩年多的時(shí)間了了,發(fā)布了四十多個(gè)版本,...
閱讀 743·2021-11-23 09:51
閱讀 2444·2021-10-11 11:10
閱讀 1316·2021-09-23 11:21
閱讀 1098·2021-09-10 10:50
閱讀 895·2019-08-30 15:54
閱讀 3335·2019-08-30 15:53
閱讀 3295·2019-08-30 15:53
閱讀 3194·2019-08-29 17:23