亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

深入探討Unit Testing in Android

2020-04-11 12:22:34
字體:
來源:轉載
供稿:網友
1. Testing for ContentProvider
在你開始為Provider寫Case之前,應該仔細讀一讀SDK文檔中關于Provider測試的說明。但是光讀那些說明,你還是沒辦法寫出正確的Case,因為你也知道,Android的文檔是比較差勁的,有一些關鍵東西文檔中沒有說明,你也知道,這在Android當中并不少見。
你寫個Provider的Case,如下:
復制代碼 代碼如下:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
}

編譯有錯誤,它說ProviderTestCase2沒有隱式的構造,看來我們需要一個構造函數,寫一個標準的JUnit構造吧!
復制代碼 代碼如下:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    public FeedProviderTest(String name) {
        super(name);
    }
}

WTF,還是有編譯錯誤,而且更嚴重!難道ProviderTestCase2不是繼承自TestCase,用了Eclipse的建議,它創建了一個帶有二個參數的構造:
復制代碼 代碼如下:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    public FeedProviderTest(String name) {
        super(name);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
        // TODO Auto-generated constructor stub
    }
}

但是僅一個名字的FeedProviderTest(String name)還是有錯誤,再試試不帶參數的,還是不行,這說明ProviderTestCase2沒有這樣的構造函數,但是沒有道理啊,因為它畢竟是繼承自TestCase的??!很神奇和詭異??!
既然ProviderTestCase2沒有一個參數的構造,那么只能去掉帶有一參數的構造了!
復制代碼 代碼如下:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

    public void testConstructor() throws Throwable {
        assertNotNull("can construct resolver", getMockContentResolver());
        ContentProvider provider = getProvider();
        assertNotNull("can instantiate provider", provider);
    }
}

寫了一個基本的測試,運行了下,得到了一個Warning,是由JUnit Framework報出來的說DemoProviderTest沒有定義公共的構造函數TestCase(name)或TestCase(),什么情況,不是我不定義而是有編譯錯誤啊,因為該死的ProviderTestCase2沒有這二個構造!該死,只能再把這個構造加回來!但是因為父類沒有,只能引用父類的雙參數的構造了!
復制代碼 代碼如下:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> { 
    public DemoProviderTest() {
        super(null, null);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

    public void testConstructor() throws Throwable {
        assertNotNull("can construct resolver", getMockContentResolver());
        ContentProvider provider = getProvider();
        assertNotNull("can instantiate provider", provider);
    }
}

但是參數傳什么呢?先用Null試試中吧!完全有錯誤,在父類的構造初始化時出現了NPE,這說明傳Null肯定是不對的!看了下強加的帶有二個參數的構造DemoProviderTest(Class<FeedProvider> providerClass, String providerAuthority),也說應該傳一個Class對象,和Provider的Authority,再試試看!
復制代碼 代碼如下:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    public DemoProviderTest() {
        super(FeedProvider.class, AUTHORITY);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

    public void testConstructor() throws Throwable {
        assertNotNull("can construct resolver", getMockContentResolver());
        ContentProvider provider = getProvider();
        assertNotNull("can instantiate provider", provider);
    }
}

這次Okay了,但是這樣一來二個參數的構造就沒有意義了,于是讓一個參數的調用二個參數的:
復制代碼 代碼如下:

    public DemoProviderTest() {
        this(FeedProvider.class, AUTHORITY);
    }

還是Okay,這說明我們的Case必須給ProviderTestCase2提供正確的構造參數!
再加上setUp和tearDown:
復制代碼 代碼如下:

    @Override
    public void setUp() throws Exception {
        mContentResolver = getMockContentResolver();
    }

    @Override
    public void tearDown() throws Exception {
        mContentResolver = null;
    }

運行,發現testConstructor掛了,說getMockContentResolver()返回的是Null,這怎么可能啊,太詭異了!想到還是可能初始化未正確,給setUp加上了父類的調用:
復制代碼 代碼如下:

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mContentResolver = getMockContentResolver();
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
        mContentResolver = null;
    }

這下再跑,全都Okay了,說明凡是涉及到重寫(Override)父類的方法,都要調用父類的方法,以期正確初始化!下面是正確的完整版:
復制代碼 代碼如下:

public class DemoProviderTest extends ProviderTestCase2<FeedProvider> {
    private ContentResolver mContentResolver;

    public DemoProviderTest() {
        this(FeedProvider.class, AUTHORITY);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mContentResolver = getMockContentResolver();
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
        mContentResolver = null;
    }

    public void testConstructor() throws Throwable {
        assertNotNull("can construct resolver", getMockContentResolver());
        ContentProvider provider = getProvider();
        assertNotNull("can instantiate provider", provider);
    }
}

總結一下,從這個例子得到的經驗是,對于組件的測試,都要繼承自android.test.*下面的組件測試框架,但是需要給這些組件測試框架傳遞正確的參數,否則Case無法測試:
二個構造函數
復制代碼 代碼如下:

    public DemoProviderTest() {
        this(FeedProvider.class, AUTHORITY);
    }

    public DemoProviderTest(Class<FeedProvider> providerClass,
            String providerAuthority) {
        super(providerClass, providerAuthority);
    }

一個都不能少,而且是JUnit的指定構造函數(帶有一個String,或不帶參數的)要調用測試架構指定的構造,以給測試框架傳遞正確的參數!
還有就是重寫的父類方法時,一定要把父類的方法也調用上,否則還是不會初始化正確!
但是這里不得不說這些組件測試框架寫的真是不好用,首先,那個名字就讓人費解,為什么有個2?。ndroid真夠2的!還有,既然作為框架,應該把初始化的工作做完整,做徹底,這樣才能稱的上框架。使用者應該只需要繼承,把自己的事情做完,就應該能進行工作,就像組件Activity或ContentProvider一樣,到了你的代碼里的時候,框架里的初始化工作已經做完,所以你,繼承者只需要關心你自已的初始化工作就好!但是測試框架就爛,繼承者不但要關心自己的初始化還要保證給父類傳遞正確的參數!
2. Testing for Activity
同樣對于Activity的測試也是要注意初始化的部分,只不過對于setUp和tearDown你不調super也沒有關系!
復制代碼 代碼如下:

public class ExplorerActivityTester extends
        ActivityInstrumentationTestCase2<ExplorerActivity> {
    public ExplorerActivityTester() {
        this(TARGET_PACKAGE_NAME, ExplorerActivity.class);
    }

    public ExplorerActivityTester(String pkg, Class<ExplorerActivity> class1) {
        super(pkg, class1);
    }

    @Override
    public void setUp() {
        mInstrumentation = getInstrumentation();
    }
}

3. Obstacles to unit testing
在Android里面,由于其系統架構的特性決定了給Android寫單元測試用例和驗證測試用例特別因難
a. Activity reuse
原因就是每一個測試的包,測試的包也是一個Apk,每一個包只能注入一個目標Apk,也就是說只能針對一個Apk里面的內容進行測試,一旦某個操作跳到了Apk以外的地方,就超出了測試框架的控制范圍。但是組件重用機制在Android中非常的普遍,通過Intent來跳到其他的應用(apk)中,調用其他應用的組件來完成某個操作,這是Android的特性,是再普遍不過的了!但這就給單元測試用例埋下了無法逾越的障礙。測試框架本身更弱,一但跳出了某個組件,Instrumentation便無法對其進行控制,開源測試框架robutium-solo一定程度上解決了這個問,Solo可以操作一個包內的任何組件,特別地它能夠解決多個Activity跳轉的問題,但是如前所述,因為一個測試Apk只能注入一個目標Apk,所以一旦Activity跳到了應用外,Solo也沒有了辦法。這是一個無解的問題。因此,Android當中做測試,只能關注一些邏輯層,API層,數據和Provider,Service等一些與表層操作較遠的代碼!對于表層Activity跳來跳去的情況,只能做部分測試,或用MockObject來解決,但是這通常失去了測試的本身意義,因為要花大量時間去創建MockObject,不值!
b. ActionBar is not clickable
還有一非常惡心的問題是,對于Activity的ActionBar無法直接點擊,真的不明白Google到底在搞什么,弄出來個新東東,竟然測試框架里面不支持操作!想到點擊ActionBar只能通過Solo來點擊屏幕坐標,這非常難以移植和維護!
說到操作,還不得不說原生框架Instrumentation支持的操作非常少,而且不好用,它只能派發KeyEvent事件,很多情況下都不好用,比如有個對話框,想要點擊Okay或是Cancel的話,就很麻煩,再如想點擊一個ListView中的某一項的話也是非常麻煩!同樣第三方的robotium-solo框架就好用多了,它進行了很好的封裝,通過Solo.clickOnText()就可以方便的點擊屏幕上的帶有此文字的View。它的內部實現方式是通過View的顯示Tree,根據Tag(文字)來查找相關的View,然后對其發送點擊事件!這也解釋了為什么Solo也無法點擊ActionBar,因為ActionBar不是在Activity的View中,它是像StatusBar一樣,屬于系統級別的東西!
c. StatusBar belongs to Settings.apk
難以想象吧,隨處可見的Statusbar竟然以屬于Settings,只有注入了Settings的包才能對Statusbar進行操作。所以雖然Statusbar上面有你的Apk的相關的東西(比如提示)但是你還是無法直接操作它,除非你寫一個專門注入Settings.apk的測試包!
4. Security Concern
測試的代碼(Instrumentation和TestRunner)也是以一個Apk的形式存在的,它可注入任何目標Apk,然后就可以對其進行操作,甚至獲取其資源和數據。這就帶來了安全上面的問題!可以把一個帶有測試代碼的Apk當成一個應用,一旦在某個手機運行,但可以操作任何一個應用。
其實,這本來不是問題,如果應用市場能對開發者上傳的應用進行嚴格的測試和審核。但是現在的問題是無論是Google Play還是其他市場都不怎么測試,所以就會讓不良者有機可乘!
其實,這里的關鍵問題在于,Android廠商不要盲目的追求數量!把應用集中銷售是Apple想出來的主意,Apple的App Store也是做的最好的!Android只是一個效仿者,所以你發展的慢,數量不多,質量不夠,收入不好,是正常的,因為你是一個追隨者,你起步晚!對于廠商來講,數量你沒有辦法控制,無法一下子弄出幾萬個應用來,這個是需要時間的,但是,至少,你可以嚴格控制質量?。∧憧梢宰龅綄ι蟼鞯膽眠M行嚴格的測試,這是對用戶負責,也是對自己負責??!所以無論是設備還是應用程序,都是Apple的要優質一些,Android總是要殘次一些,所以你看Apple的東西價格就高,Android就便宜,當然價格也是Android的唯一優勢!現的社會是一分錢一分貨,便宜自然就沒好貨!
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩av在线天堂网| 亚洲一区亚洲二区| 日本a级片电影一区二区| 欧美日韩国产色| 欧美激情一区二区三区在线视频观看| 97香蕉久久夜色精品国产| 久久久久亚洲精品成人网小说| 欧美刺激性大交免费视频| 国产精品第100页| 91黄色8090| 九九热精品视频国产| 91九色单男在线观看| 欧美日韩国产麻豆| 日韩欧美在线视频免费观看| 国产精品久久久一区| 亚洲视频网站在线观看| 日韩精品一二三四区| 国产丝袜一区二区三区免费视频| 俺去亚洲欧洲欧美日韩| 亚洲r级在线观看| 97超碰国产精品女人人人爽| 成人黄色短视频在线观看| 亚洲久久久久久久久久| www日韩中文字幕在线看| 精品人伦一区二区三区蜜桃网站| 亚洲一区二区中文字幕| 亚洲美女av网站| 欧美视频中文在线看| 日韩中文综合网| 日韩在线高清视频| 亚洲美女av网站| 欧美中文字幕在线观看| 亚洲第一av网| 日本久久久久亚洲中字幕| 日韩精品视频在线播放| 久久久免费在线观看| 国产精品视频资源| 亚洲精品在线观看www| 欧美国产日韩精品| 日韩免费在线播放| 久精品免费视频| 国产精品色悠悠| 久久99国产综合精品女同| 亚洲一区亚洲二区| 日韩精品视频免费| 国产在线精品一区免费香蕉| 亚洲一区二区久久久| 最新的欧美黄色| 亚洲精品av在线| 欧美裸体视频网站| 亚洲欧美日韩视频一区| 欧美精品做受xxx性少妇| 在线观看亚洲区| 亚洲最大的av网站| 欧美性生活大片免费观看网址| 国产欧美欧洲在线观看| 97涩涩爰在线观看亚洲| 亚洲福利视频在线| 亚洲电影天堂av| 日本精品一区二区三区在线| 国产精品免费网站| 日韩电影中文字幕在线| 91在线视频九色| 欧美最猛性xxxxx亚洲精品| 欧美疯狂性受xxxxx另类| 成人做爰www免费看视频网站| 福利二区91精品bt7086| 亚洲一区二区国产| 欧美成人精品在线观看| 免费av在线一区| 亚洲成在人线av| 欧美日韩国产成人在线| 国内精品久久久久影院优| 日本a级片电影一区二区| 国产精品扒开腿爽爽爽视频| 亚洲成人免费在线视频| 成人欧美一区二区三区在线| 精品视频在线播放色网色视频| 在线成人一区二区| 国产精品一区专区欧美日韩| 色妞色视频一区二区三区四区| 日韩欧美在线第一页| 欧美久久精品一级黑人c片| 中文字幕精品www乱入免费视频| 一区二区三区www| 欧美成人午夜影院| 日韩av影视综合网| 日韩精品免费在线观看| 日本免费在线精品| 国产主播精品在线| 久久色精品视频| 亚洲www在线观看| 一本色道久久综合狠狠躁篇的优点| 96pao国产成视频永久免费| 亚洲国产另类久久精品| 国产精品久久电影观看| 亚洲综合中文字幕68页| 日韩av电影手机在线| 日韩欧美国产高清91| 亚洲片国产一区一级在线观看| 久久精品99久久香蕉国产色戒| 久久久国产精彩视频美女艺术照福利| 一本色道久久综合狠狠躁篇怎么玩| 午夜免费在线观看精品视频| 久久躁日日躁aaaaxxxx| 国产一区二区三区在线免费观看| 国产视频精品自拍| 亚洲深夜福利视频| 97视频网站入口| 精品久久久久久久久久久久| 琪琪第一精品导航| 久久视频在线视频| 久久久精品国产| 亚洲美女自拍视频| 欧美午夜无遮挡| 久久精品美女视频网站| 福利视频导航一区| 狠狠色噜噜狠狠狠狠97| 国产精品劲爆视频| 久久久久免费精品国产| 亚洲一区二区三区四区视频| 国产精品你懂得| 国产精品美女久久久免费| 亚洲激情第一页| 欧美主播福利视频| 国内精品模特av私拍在线观看| 欧美激情视频一区二区三区不卡| 黄色成人在线免费| 欧美日韩一区二区免费在线观看| 欧美成人免费播放| 中日韩美女免费视频网址在线观看| 精品成人av一区| 欧美另类精品xxxx孕妇| 日韩美女福利视频| 成人日韩av在线| 国产精品一香蕉国产线看观看| 国模精品视频一区二区三区| 少妇激情综合网| 亚洲第一网站男人都懂| 亚洲精品一区二三区不卡| 精品高清一区二区三区| 久久久久久国产三级电影| 国产亚洲精品久久| 欧美激情精品久久久久久大尺度| 日韩电影在线观看免费| 国产精品成人免费电影| 狠狠躁夜夜躁人人爽天天天天97| 精品福利樱桃av导航| 亚洲国产成人91精品| 久久精品一偷一偷国产| 日韩欧美成人区| 夜色77av精品影院| 一夜七次郎国产精品亚洲| 欧美精品国产精品日韩精品| 日本一区二三区好的精华液| 久久精品一本久久99精品| 97免费视频在线| 日本午夜在线亚洲.国产| 成人国产在线激情| 国产一区二区三区在线视频| 狠狠综合久久av一区二区小说| 91超碰中文字幕久久精品| 韩国一区二区电影| 日韩在线资源网|