Android 單元測(cè)試和 UI 測(cè)試初步實(shí)踐_第1頁(yè)
Android 單元測(cè)試和 UI 測(cè)試初步實(shí)踐_第2頁(yè)
Android 單元測(cè)試和 UI 測(cè)試初步實(shí)踐_第3頁(yè)
Android 單元測(cè)試和 UI 測(cè)試初步實(shí)踐_第4頁(yè)
Android 單元測(cè)試和 UI 測(cè)試初步實(shí)踐_第5頁(yè)
已閱讀5頁(yè),還剩5頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第第頁(yè)Android單元測(cè)試和UI測(cè)試初步實(shí)踐

采用Mockito框架mock一個(gè)測(cè)試用的DataManager和V層代理NewsContract.View。所謂的mock就是創(chuàng)建一個(gè)類(lèi)的虛假的對(duì)象,在測(cè)試環(huán)境中,用來(lái)替換掉真實(shí)的對(duì)象,以達(dá)到驗(yàn)證對(duì)象方法調(diào)用情況,或是指定這個(gè)對(duì)象的某些方法返回特定的值等;@Before注解的方法會(huì)在執(zhí)行測(cè)試用例之前執(zhí)行,這里做一個(gè)初始化的操作,主要是Mockito框架的初始化及presenter的初始化;@After注解的方法會(huì)在執(zhí)行測(cè)試用例之后執(zhí)行,這里做一個(gè)presenter的detach()操作,防止出現(xiàn)內(nèi)存泄露等問(wèn)題;@Test注解的方法是實(shí)際執(zhí)行的(測(cè)試方法)。這里根據(jù)之前的業(yè)務(wù)代碼邏輯:環(huán)境初始化:由于NewsPresenter的業(yè)務(wù)邏輯中是需要DataManager返回一個(gè)NewsResultBean實(shí)例才能進(jìn)行后續(xù)的操作,而mock的話(huà)只能返回一個(gè)空對(duì)象,所以在代碼前兩行筆者通過(guò)Mockito的when()方法,在程序調(diào)用DataManager#getNews()方法時(shí)返回一個(gè)空的NewsResultBean實(shí)例。執(zhí)行操作:執(zhí)行P層的NewsPresenter#getNews()。在業(yè)務(wù)邏輯中,執(zhí)行此方法之后,會(huì)先調(diào)用DataManager#getNews(),然后將數(shù)據(jù)交給V層的代理。驗(yàn)證結(jié)果正確性:一般來(lái)說(shuō),我們要驗(yàn)證一個(gè)方法執(zhí)行結(jié)果是否正確,最簡(jiǎn)單的方法的就是看執(zhí)行完的方法輸出是否與預(yù)期輸出相一致。但在這里,NewsPresenter#getNews()為一個(gè)void方法,沒(méi)有返回值,那么該怎么驗(yàn)證呢?其實(shí)這個(gè)方法也是有輸出的,輸出就是:調(diào)用了DataManager#getNews()方法,獲取到數(shù)據(jù)后調(diào)用NewsContract.View#showNews(news)顯示數(shù)據(jù)。所以這里主要驗(yàn)證的是DataManager#getNews()和NewsContract.View#showProgress(),NewsContract.View#showNews(news)和NewsContract.View#hideProgress()這三個(gè)方法是否有被調(diào)用到,這里運(yùn)用到Mockito的verify()方法。至此,一個(gè)Android的單元測(cè)試用例編寫(xiě)完成。通過(guò)AndroidStudio直接運(yùn)行此單元測(cè)試用例,結(jié)果如下:

需要明白一個(gè)點(diǎn):?jiǎn)卧獪y(cè)試它只是測(cè)試一個(gè)方法單元,它不是測(cè)試一整個(gè)APP的功能流程,即單元測(cè)試不會(huì)涉及到(數(shù)據(jù)庫(kù))或網(wǎng)絡(luò)等復(fù)雜的外部環(huán)境。比如說(shuō)這里我們只測(cè)試到NewsPresenter#getNews()方法,并沒(méi)有測(cè)試NewsFragment的整個(gè)初始化到顯示的過(guò)程是否正常,數(shù)據(jù)是否有誤。(這樣的測(cè)試往往稱(chēng)之為集成測(cè)試)

AndroidUI測(cè)試實(shí)踐

創(chuàng)建新用例如果要編寫(xiě)一個(gè)新的本地UI測(cè)試用例,只需打開(kāi)你想測(cè)試的java代碼文件,然后點(diǎn)擊類(lèi)名??T(Windows:Ctrl+Shift+T)選擇要生成的方法選擇androidTest文件夾,對(duì)應(yīng)于本地UI測(cè)試完成。

增加依賴(lài)庫(kù)需要Espresso框架支持,所以在build.gradle中增加(注意是androidTestImplementation):

androidTestImplementationandroidx.test:runner:1.1.0androidTestImplementationandroidx.test:rules:1.1.0androidTestImplementationandroidx.test.espresso:espresso-core:3.0.2androidTestImplementationandroidx.test.espresso:espresso-contrib:3.0.2androidTestImplementationandroidx.test.espresso:espresso-intents:3.0.2androidTestImplementationandroidx.test.espresso.idling:idling-concurrent:3.0.2androidTestImplementationandroidx.test.espresso:espresso-idling-resource:3.0.2

編寫(xiě)測(cè)試代碼筆者主要測(cè)試的代碼為NewsDetailActivity,主要功能是加載intent傳遞過(guò)來(lái)的新聞標(biāo)題和新聞原文地址,然后在Toolbar中顯示新聞標(biāo)題,在(Web)view中加載此新聞。

對(duì)應(yīng)著,實(shí)際編寫(xiě)測(cè)試代碼的時(shí)候,可以構(gòu)造一個(gè)測(cè)試用的intent,在intent中加入需要的測(cè)試數(shù)據(jù),然后啟動(dòng)這個(gè)activity,檢查數(shù)據(jù)是否正確即可。這里我們借助Espresso框架,它有三個(gè)重要的組成部分:ViewMatchers(根據(jù)視圖id或其他屬性匹配指定的View),ViewActions(執(zhí)行View的某些行為,例如點(diǎn)擊事件),ViewAssertions(檢查View的某些狀態(tài),例如指定View是否顯示在屏幕上)。

新創(chuàng)建的UI測(cè)試代碼如下:

/***CreatedbyXuon2023/04/09.*/@RunWith(AndroidJUnit4.class)@LargeTestpublicclassNewsDetailActivityTest{@RulepublicActivityTestRuleNewsDetailActivitynewsDetailActivityActivityTestRule=newActivityTestRule(NewsDetailActivity.class,true,false);@BeforepublicvoidsetUp(){Intentintent=newIntent(InstrumentationRegistry.getInstrumentation().getTargetContext(),NewsDetailActivity.class);intent.putExtra(Constants.NEWS_URL,TestConstants.NEWS_DETAIL_ACTIVITY_TEST_URL);intent.putExtra(Constants.NEWS_IMG,TestConstants.NEWS_DETAIL_ACTIVITY_TEST_IMG);intent.putExtra(Constants.NEWS_TITLE,TestConstants.NEWS_DETAIL_ACTIVITY_TEST_TITLE);newsDetailActivityActivityTestRule.launchActivity(intent);IdlingRegistry.getInstance().register(newsDetailActivityActivityTestRule.getActivity().getCountingIdlingResource());}@TestpublicvoidshowNewsDetail(){onView(withId(R.id.toolbar)).check(matches(isDisplayed()));onView(withId(R.id.iv_news_detail_pic)).check(matches(isDisplayed()));onView(withId(R.id.clp_toolbar)).check(matches(isDisplayed()));onView(withId(R.id.clp_toolbar)).check(matches(withCollapsingToolbarLayoutText(is(TestConstants.NEWS_DETAIL_ACTIVITY_TEST_TITLE))));}@AfterpublicvoidtearDown(){IdlingRegistry.getInstance().unregister(newsDetailActivityActivityTestRule.getActivity().getCountingIdlingResource());}}

在其中:

在類(lèi)聲明的開(kāi)頭,添加了兩個(gè)注解@RunWith(AndroidJUnit4.class)和@LargeTest;@RunWith注解可以改變JUnit測(cè)試用例的的默認(rèn)執(zhí)行類(lèi),由于這里是需要Android環(huán)境且使用到Espresso框架,所以@RunWith選擇AndroidJUnit4類(lèi)。@LargeTest表示此測(cè)試用例會(huì)使用到外部文件系統(tǒng)或者網(wǎng)絡(luò),并且運(yùn)行時(shí)間大于1000ms。

聲明了一個(gè)變量newsDetailActivityActivityTestRule并用@Rule注解,newsDetailActivityActivityTestRule是ActivityTestRule的實(shí)例化對(duì)象。ActivityTestRule主要用來(lái)測(cè)試單個(gè)Activity,這個(gè)Activity將在@Test和@Before前啟動(dòng)。它其中包含一些基礎(chǔ)功能,例如啟動(dòng)Activity,獲取當(dāng)前Activity實(shí)例等;同樣的,這里@Before注解的方法會(huì)在執(zhí)行測(cè)試用例之前執(zhí)行,這里構(gòu)造一個(gè)測(cè)試用intent,最后通過(guò)newsDetailActivityActivityTestRule#launchActivity(intent)方法啟動(dòng)待測(cè)試Activity,并做一個(gè)IdlingResource的綁定;@After注解的方法會(huì)在執(zhí)行測(cè)試用例之后執(zhí)行,這里做一個(gè)IdlingResource的解綁操作;什么是IdlingResource呢?

通常來(lái)說(shuō),大多數(shù)APP在設(shè)計(jì)業(yè)務(wù)功能的過(guò)程中,會(huì)有很多的異步任務(wù),例如使用Rxjava發(fā)起網(wǎng)絡(luò)請(qǐng)求等,但是Espresso并不知道你的異步任務(wù)什么時(shí)候結(jié)束,如果單純使用Thread.sleep()等待異步回調(diào)的結(jié)果又過(guò)于"硬核',所以需要借助于IdlingResource這個(gè)類(lèi)。

它需要在業(yè)務(wù)代碼中添加相關(guān)的邏輯。例如在NewsDetailActivity中,會(huì)接收到intent傳遞過(guò)來(lái)的新聞圖片地址,然后使用Glide異步加載此圖片,大致代碼如下:

publicclassNewsDetailActivityextendsAppCompatActivity{@BindView(R.id.iv_news_detail_pic)privateImageViewivNewsDetailPic;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_news);//省略部分代碼邏輯//開(kāi)始發(fā)起異步操作,App開(kāi)始進(jìn)入忙碌狀態(tài)EspressoIdlingResource.increment();//開(kāi)始加載圖片Glide.with(context).asBitmap().load(imgUrl).into(newGlideDrawableImageViewTarget(mAvatar){@OverridepublicvoidonResourceReady(@NonNullBitmapresource,@NullableTransition?superBitmaptransition){super.onResourceReady(resource,transition);//異步操作結(jié)束,將App設(shè)置成空閑狀態(tài)if(!EspressoIdlingResource.getIdlingResource().isIdleNow()){EspressoIdlingResource.decrement();}}});}//省略代碼@VisibleForTestingpublicIdlingResourcegetCountingIdlingResource(){returnEspressoIdlingResource.getIdlingResource();}}publicclassEspressoIdlingResource{privatestaticfinalStringRESOURCE=GLOBAL;//Espresso提供了一個(gè)實(shí)現(xiàn)好的CountingIdlingResource類(lèi)//如果沒(méi)有特別(需求)的話(huà),直接使用它即可privatestaticCountingIdlingResourcecountingIdlingResource=newCountingIdlingResource(RESOURCE);publicstaticvoidincrement(){countingIdlingResource.increment();}publicstaticvoiddecrement(){countingIdlingResource.decrement();}publicstaticIdlingResourcegetIdlingResource(){if(countingIdlingResource==null){countingIdlingResource=newCountingIdlingResource(RESOURCE);}returncountingIdlingResource;}}

再加上我們?cè)跍y(cè)試代碼中聲明的IdlingRegistry.getInstance().register()和IdlingRegistry.getInstance().unregister()方法,根據(jù)APP是否處于忙碌狀態(tài)來(lái)判斷異步任務(wù)是否完成,這樣Espresso就能做到對(duì)異步任務(wù)進(jìn)行相應(yīng)的測(cè)試。

@Test注解的方法是實(shí)際執(zhí)行的測(cè)試方法。這里根據(jù)之前的業(yè)務(wù)代碼邏輯:環(huán)境初始化:模擬了測(cè)試的intent數(shù)據(jù)執(zhí)行操作:加載intent傳遞過(guò)來(lái)的數(shù)據(jù)驗(yàn)證結(jié)果正確性:檢查對(duì)應(yīng)的UI樣式是否正常顯示測(cè)試數(shù)據(jù),這里主要利用Espresso的幾個(gè)重要的API:onView():獲得視圖view,這里通過(guò)withId()方法搜索,即根據(jù)id來(lái)獲取對(duì)應(yīng)的viewcheck():檢驗(yàn)視圖view,可以檢查視圖文本是否匹配或者視圖是否顯示等,主要依靠match()方法返回對(duì)應(yīng)的匹配類(lèi),Espresso也自帶很多已封裝好的ViewMatchers供使用以鏈?zhǔn)酱a的形式編寫(xiě)驗(yàn)證測(cè)試結(jié)果的代碼,例如onView(withId(R.id.toolbar)).check(matches(isDisplayed()));意思就是獲取id為R.id.toolbar的view,檢查這個(gè)view是否正常顯示。

如果Espresso自帶的ViewMatchers不能滿(mǎn)足需求的話(huà),我們也可以自定義一個(gè)matcher,例如onView(withId(R.id.clp_toolbar)).check(matches(withCollapsingToolbarLayoutText(is(TestConstants.NEWS_DETAIL_ACTIVITY_TEST_TITLE))));,我們獲取到的view是一個(gè)CollapsingToolbarLayout,是一個(gè)特殊樣式的Toolbar,我們要檢查其中的標(biāo)題是否與測(cè)試數(shù)據(jù)相匹配,我們可以編寫(xiě)自定義的Matcher:

publicstaticMatcherViewwithCollapsingToolbarLayoutText(MatcherStringstringMatcher){returnnewBoundedMatcherView,CollapsingToolbarLayout(CollapsingToolbarLayout.class){@OverridepublicvoiddescribeTo(D

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論