成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Android單元測(cè)試 - 幾個(gè)重要問(wèn)題

ChristmasBoy / 395人閱讀

摘要:言歸正傳,上一篇文章單元測(cè)試如何開(kāi)始介紹了幾款單元測(cè)試框架基本用法依賴隔離概念,本篇主要解答單元測(cè)試中幾個(gè)重要問(wèn)題。在單元測(cè)試交流微信群,很多新進(jìn)來(lái)的小伙伴,都會(huì)幾個(gè)大同小異的問(wèn)題。

原文鏈接:http://www.jianshu.com/p/f5d197a4d83a

前言

已經(jīng)一個(gè)月沒(méi)寫文章了,由于9月份在plan國(guó)慶旅行計(jì)劃,國(guó)慶前前后后去了14天旅行,所以沒(méi)時(shí)間寫,哈哈。

言歸正傳,上一篇文章《Android單元測(cè)試 - 如何開(kāi)始?》介紹了幾款單元測(cè)試框架、Junit & Mockito基本用法、依賴隔離 & Mock概念,本篇主要解答單元測(cè)試中幾個(gè)重要問(wèn)題。

在單元測(cè)試交流微信群,很多新進(jìn)來(lái)的小伙伴,都會(huì)幾個(gè)大同小異的問(wèn)題。我們幾個(gè)老鳥(niǎo)們答完一次又一次(厚顏無(wú)恥地把自己算上^_^),筆者是有點(diǎn)不耐煩了,后來(lái)就等其他同學(xué)回答他們.....其實(shí)大家提的問(wèn)題,歸根到底就是“依賴問(wèn)題”,jvm依賴還是android依賴?用到native方法報(bào)錯(cuò)怎么辦?靜態(tài)方法怎么解決?

于是呢,筆者決定專門寫一篇文章,來(lái)講解這幾個(gè)問(wèn)題。

如何解決Android依賴?

隔離Native方法

解決內(nèi)部new對(duì)象

靜態(tài)方法

RxJava異步轉(zhuǎn)同步

1.如何解決Android依賴?

小白:“Presenter中用到TextUtils,運(yùn)行junit時(shí)報(bào)"java.lang.RuntimeException: Method isEmpty in android.text.TextUtils not mocked"錯(cuò)誤... 是不是要用robolectric?”

別急,還未到robolectric出場(chǎng)的時(shí)候呢!

由于junit運(yùn)行在jvm上,而jdk沒(méi)有android源碼,所以TextUtils這些在android sdk中的類,運(yùn)行junit時(shí)就引用不上了。既然jdk沒(méi)有,我們就自己加唄!

test/java目錄下,創(chuàng)建android.text.TextUtils

package android.text;

public class TextUtils {

    public static boolean isEmpty(CharSequence str) {
        if (str == null || str.equals("")) {
            return true;
        }
        return false;
    }
}

關(guān)鍵是要個(gè)TextUtils同包名、同類名、同方法名。注意不是main/java下創(chuàng)建,不然會(huì)提示Duplicate class found in the file...。單元測(cè)試運(yùn)行妥妥的:

原理很簡(jiǎn)單,jvm運(yùn)行時(shí)會(huì)找android.text.TextUtils類,然后找isEmpty方法執(zhí)行。學(xué)過(guò)java反射的同學(xué)都知道,只要知道包名類名,就可以拿到Class,知道該類某方法名,就可以獲取Method并執(zhí)行。jvm也是類似的機(jī)制,只要我們給一個(gè)包名類名與android sdk相同的類,寫上方法名&參數(shù)&返回值相同的方法,jvm就能編譯并執(zhí)行。

(提示:android的View之類也能這么搞噢)

2.隔離Native方法

小白:“我用到native方法,junit運(yùn)行失敗,robolectric也不支持加載so文件,怎么辦?”

Model類:

package com.test.unit;

public class Model {
    public native boolean nativeMethod();
}

單元測(cè)試:

public class ModelTest {

    Model model;

    @Before
    public void setUp() throws Exception {
        model = new Model();
    }

    @Test
    public void testNativeMethod() throws Exception {
        Assert.assertTrue(model.nativeMethod());
    }
}

run ModelTest... 報(bào)錯(cuò)java.lang.UnsatisfiedLinkError: com.test.unit.Model.nativeMethod()

上篇文章《Android單元測(cè)試 - 如何開(kāi)始?》講述的“依賴隔離”,這里要用到了!

改進(jìn)單元測(cè)試:

public class ModelTest {

    Model model;

    @Before
    public void setUp() throws Exception {
        model = mock(Model.class);
    }

    @Test
    public void testNativeMethod() throws Exception {
        when(model.nativeMethod()).thenReturn(true);

        Assert.assertTrue(model.nativeMethod());
    }
}

run一下,pass了:

這里稍微講講java查找native方法的過(guò)程:
1.Model.java全名是com.test.unit.Model.java;
2.調(diào)用native方法nativeMethod()后, jvm會(huì)去找C++層com_test_unit_Model.cpp,再找com_test_unit_Model_nativeMethod()方法,并調(diào)用。

在APP運(yùn)行過(guò)程,我們會(huì)把cpp編譯成so文件,然后讓APP加載到dalvik虛擬機(jī)。但在單元測(cè)試中,沒(méi)有加載對(duì)應(yīng)的so文件,也沒(méi)有編譯cpp呀!大牛們可能會(huì)嘗試單元測(cè)試時(shí)加載so文件,但完全沒(méi)有必要,也不符合單元測(cè)試的原則。

所以,我們可以直接用Mockito框架mock native方法就行啦。實(shí)際上,不僅僅是native方法需要mock,很多依賴的方法、類都要mock,下面會(huì)講到更常用的場(chǎng)景。

(參考《Android JNI原理分析》)

3.解決內(nèi)部new對(duì)象

小白:“我在Presenter里new Model,Model依賴比較多,會(huì)做sql操作,等等.....Presenter依賴Model返回結(jié)果,導(dǎo)致Presenter沒(méi)法單元測(cè)試?yán)?!求大神指點(diǎn)!”

小白C的例子:
Model:

public class Model {
    public boolean getBoolean() {
        boolean bo = ....... // 一堆依賴,代碼很復(fù)雜
        return bo;
    }
}

Presenter:

public class Presenter {

    Model model;

    public Presenter() {
        model = new Model();
    }

    public boolean getBoolean() {
        return model.getBoolean());
    }
}

錯(cuò)誤的單元測(cè)試:

public class PresenterTest {

    Presenter presenter;

    @Before
    public void setUp() throws Exception {
        presenter = new Presenter();
    }

    @Test
    public void testGetBoolean() throws Exception {
        Assert.assertTrue(presenter.getBoolean());
    }
}

還是那句話:依賴隔離。我們隔離Model依賴,即mock Model對(duì)象,而不是new Model()。

找找以上PresenterTest的問(wèn)題吧:PresenterTest完全不知道Model的存在,意思是無(wú)法mock Model。那么,我們就想辦法把mock Model傳給Presenter——在Presenter構(gòu)造函數(shù)傳參!

改進(jìn)Presenter

public class Presenter {

    Model model;

    public Presenter(Model model) {
        this.model = model;
    }

    public boolean getBoolean() {
        return model.getBoolean();
    }
}

正確的單元測(cè)試:

public class PresenterTest {
    Model     model;
    Presenter presenter;

    @Before
    public void setUp() throws Exception {
        model = mock(Model.class);// mock Model對(duì)象

        presenter = new Presenter(model);
    }

    @Test
    public void testGetBoolean() throws Exception {
        when(model.getBoolean()).thenReturn(true);

        Assert.assertTrue(presenter.getBoolean());
    }
}

事情就這么解決了。如果你覺(jué)得在Activity直接用默認(rèn)Presenter構(gòu)造函數(shù),在構(gòu)造函數(shù)new Model()比較方便,那就保留默認(rèn)構(gòu)造函數(shù)唄。當(dāng)然使用dagger2就不存在多個(gè)構(gòu)造函數(shù)了,都是構(gòu)造傳參。

4.靜態(tài)方法

小白:“大神,我在Presenter用到靜態(tài)方法....”
筆者:“行了,知道你要說(shuō)什么?!?/p>

Presenter:

public class Presenter {

    public String getSignParams(int uid, String name, String token) {
        return SignatureUtils.sign(uid, name, token);
    }
}

解決方法跟上面【解決內(nèi)部new對(duì)象】大同小異,核心思想還是依賴隔離

1.把sign(...)改成非靜態(tài)方法;
2.把SignatureUtils作為成員變量;
3.構(gòu)造方法傳入SignatureUtils;
4.單元測(cè)試時(shí),把mock SignatureUtils傳給Presenter。

改進(jìn)后Presenter

public class Presenter {
    SignatureUtils mSignUtils;

    public Presenter(SignatureUtils signatureUtils) {
        this.mSignUtils= signatureUtils;
    }

    public String getSignParams(int uid, String name, String token) {
        return mSignUtils.sign(uid, name, token);
    }
}
5.RxJava異步轉(zhuǎn)同步

小白:“大神...”
筆者:“為師掐指一算,料汝會(huì)遇此劫難。”
小白:(傳說(shuō)中從入門到出家?)

public class RxPresenter {

    public void testRxJava(String msg) {
        Observable.just(msg)
                  .subscribeOn(Schedulers.io())
                  .delay(1, TimeUnit.SECONDS) // 延時(shí)1秒
//                  .observeOn(AndroidSchedulers.mainThread())
                  .subscribe(new Action1() {
                      @Override
                      public void call(String msg) {
                          System.out.println(msg);
                      }
                  });
    }
}

單元測(cè)試

public class RxPresenterTest {

    RxPresenter rxPresenter;

    @Before
    public void setUp() throws Exception {
        rxPresenter = new RxPresenter();
    }

    @Test
    public void testTestRxJava() throws Exception {
        rxPresenter.testRxJava("test");
    }
}

運(yùn)行RxPresenterTest

你會(huì)發(fā)現(xiàn)沒(méi)有輸出"test",為什么呢?

由于testRxJava里面,Obserable.subscribeOn(Schedulers.io())把線程切換到io線程,并且delay了1秒,而testTestRxJava()單元測(cè)試早已在當(dāng)前線程跑完了。筆者試過(guò),即使去掉delay(1, TimeUnit.SECONDS),還是不會(huì)輸出‘test’。

可以看到筆者把.observeOn(AndroidSchedulers.mainThread())注釋掉了,我們把那句代碼加上,再跑一下testTestRxJava(),會(huì)報(bào)java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.

這是由于jdk沒(méi)有android.os.Looper這個(gè)類及相關(guān)依賴。

解決以上兩個(gè)問(wèn)題,我們只要把Schedulers.io()&AndroidSchedulers.mainThread()切換為Schedulers.immediate()就可以了。RxJava開(kāi)發(fā)團(tuán)隊(duì)已經(jīng)為大家想好了,提供了RxJavaHooksRxAndroidPlugins兩個(gè)hook操作的類。

新建RxTools

public class RxTools {
    public static void asyncToSync() {
        Func1 schedulerFunc = new Func1() {
            @Override
            public Scheduler call(Scheduler scheduler) {
                return Schedulers.immediate();
            }
        };

        RxAndroidSchedulersHook rxAndroidSchedulersHook = new RxAndroidSchedulersHook() {
            @Override
            public Scheduler getMainThreadScheduler() {
                return Schedulers.immediate();
            }
        };

        RxJavaHooks.reset();
        RxJavaHooks.setOnIOScheduler(schedulerFunc);
        RxJavaHooks.setOnComputationScheduler(schedulerFunc);

        RxAndroidPlugins.getInstance().reset();
        RxAndroidPlugins.getInstance().registerSchedulersHook(rxAndroidSchedulersHook);
    }
}

RxPresenterTest.setUp()加一句RxTools.asyncToSync();:

public class RxPresenterTest {
    RxPresenter rxPresenter;

    @Before
    public void setUp() throws Exception {
        rxPresenter = new RxPresenter();

        RxTools.asyncToSync();
    }
    ...
}

再跑一次testTestRxJava()

總算輸出"test",感謝上帝啊?。☉?yīng)該打賞下筆者吧^_^)

讀者有沒(méi)發(fā)現(xiàn)RxTools.asyncToSync()多加了一句RxJavaHooks.setOnComputationScheduler(schedulerFunc),意思將computation線程切換為immediate線程。筆者發(fā)現(xiàn),僅僅添加RxJavaHooks.setOnIOScheduler(schedulerFunc),對(duì)于有delayObserable還是未通過(guò),于是順手把computation線程也切換了,于是就可以了。

還有RxJavaHooks.reset()RxAndroidPlugins.getInstance().reset(),筆者發(fā)現(xiàn),當(dāng)運(yùn)行大量單元測(cè)試時(shí),有些會(huì)失敗,但多帶帶運(yùn)行失敗的單元測(cè)試,又通過(guò)了。百思不得其解后,添加了那兩句.....可以了!

(關(guān)于RxJavaHooksRxAndroidPlugins的使用,在很久前的文章 《(MVP+RxJava+Retrofit)解耦+Mockito單元測(cè)試 經(jīng)驗(yàn)分享》已經(jīng)提及過(guò))

小結(jié)

筆者:“小白同學(xué),現(xiàn)在你踩過(guò)的坑,填好未?”
小白:“方丈,啊不,大神,上面幾個(gè)問(wèn)題是解決了,不過(guò)還有其他問(wèn)題?!?br>筆者:“不挖坑,怎么填坑呢?以后再給你講講其他單元測(cè)試的玄機(jī)?!?br>小白:“......”

本文詳述了幾個(gè)單元測(cè)試重要問(wèn)題的解決方法,讀者不難發(fā)現(xiàn),筆者一直強(qiáng)調(diào) 依賴隔離、依賴隔離、依賴隔離,這個(gè)概念在單元測(cè)試中相當(dāng)重要。還搞不懂這個(gè)概念的同學(xué),看多幾次《Android單元測(cè)試 - 如何開(kāi)始?》(又厚顏無(wú)恥地廣告),同時(shí)在實(shí)踐中不斷回顧這個(gè)理念。

只要解決好這幾個(gè)問(wèn)題,Presenter單元測(cè)試就不難了。還有本文未提及的sqlite、SharedPreferences單元測(cè)試、在后面的文章會(huì)給讀者介紹下。

感謝讀者對(duì)筆者一直以來(lái)的支持,麻煩點(diǎn)贊&隨手轉(zhuǎn)發(fā),好人一世平安。

關(guān)于作者

我是鍵盤男。
在廣州生活,在創(chuàng)業(yè)公司上班,猥瑣文藝碼農(nóng)。喜歡科學(xué)、歷史,玩玩投資,偶爾獨(dú)自旅行。希望成為獨(dú)當(dāng)一面的工程師。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/65193.html

相關(guān)文章

  • Android單元測(cè)試 - 如何開(kāi)始?

    摘要:寫單元測(cè)試時(shí),應(yīng)該把這些依賴隔離,讓每個(gè)單元保持獨(dú)立。以上的各種原因,都會(huì)影響單元測(cè)試的結(jié)果。在單元測(cè)試的基礎(chǔ)上,將相關(guān)模塊組合成為子系統(tǒng)或系統(tǒng)進(jìn)行測(cè)試,稱為集成測(cè)試。可以看到,單元測(cè)試速度比集成測(cè)試,也叫測(cè)試要快,并且開(kāi)發(fā)成本也是最低。 showImg(/img/remote/1460000006811144); 原文鏈接:http://www.jianshu.com/p/bc996...

    Developer 評(píng)論0 收藏0
  • 簡(jiǎn)單易操作的跨瀏覽器 JavaScript 單元測(cè)試解決方案

    摘要:好的單元測(cè)試全面的功能拋錯(cuò)和邊緣覆蓋可以成為項(xiàng)目開(kāi)發(fā)或修改完成后是否能安全上線的重要判斷依據(jù)之一。目前國(guó)內(nèi)外的云測(cè)試工具和方案有很多,因?yàn)樵茰y(cè)試的實(shí)質(zhì)是運(yùn)行遠(yuǎn)程虛擬機(jī),需要大量的服務(wù)器和齊全的設(shè)備,所以大多數(shù)都是收費(fèi)的。 關(guān)于單元測(cè)試 前端的單元測(cè)試也可以稱為自動(dòng)化測(cè)試,測(cè)試驅(qū)動(dòng)開(kāi)發(fā),單元測(cè)試對(duì)于前端模塊化、框架和功能庫(kù)的開(kāi)發(fā)是非常有必要的,只要做好模塊的解耦和功能劃分,單元測(cè)試就可以...

    Xufc 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<