




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
【移動應(yīng)用開發(fā)技術(shù)】android單元測試的方法是什么
今天在下給大家分享一下android單元測試的方法是什么的相關(guān)知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。正文首先我們從Model層開始,通過具體代碼來詳盡說明一下一個單元測試覆蓋率100%的測試工程是如何建立的。嚴格意義上講,Model數(shù)據(jù)層負責(zé)數(shù)據(jù)加載與儲存,是游離于安卓環(huán)境之外的存在,所以它可以不需要借助安卓SDK的支持。使用Junit結(jié)合Mockito即可做到100%條件分支覆蓋率的單元測試。如果項目的Model層有安卓依賴,可能就表明此處的代碼需要重構(gòu)了,這也是單元測試其中的一個意義,讓代碼邏輯更清晰。清除Model層的安卓依賴的另一層面好處是讓測試case更高效,含有android依賴的測試case執(zhí)行最快也需要5秒,但對于一個沒有安卓依賴的Model類,跑完全部case的時間可以降低至毫秒級。所以,去除Model層所不需要的安卓依賴還是很有必要的。代碼Model層測試代碼如下:@RunWith(MockitoJUnitRunner.class)
public
classWeatherModelTest
{
privateWeatherModelmodel;
@Mock
ApiServiceapi;
@Mock
WeatherDataConvertconvertData;
@Mock
WeatherRequestListenerlistener;
private
static
finalStringJSON_ROOT_PATH="/json/";
privateStringjsonFullPath;
privateWeatherDatanetData;
privateMapqueryMap;
@Before
public
voidsetUp()
{
RxUnitTestTools.openRxTools();
model=newWeatherModel();
}
private
voidinitResponse()
{
try{
jsonFullPath=
getClass().getResource(JSON_ROOT_PATH).toURI().getPath();
}
catch(URISyntaxException
e)
{
e.printStackTrace();
}
String
json
=
getResponseString("weather.json");
Gson
gson
=newGson();
netData=
gson.fromJson(json,WeatherData.class);
model.setApiService(api);
try{
Field
field
=
WeatherModel.class.getDeclaredField("convert");
field.setAccessible(true);
field.set(model,convertData);
}
catch(Exception
e)
{
//reflect
error
}
queryMap=newHashMap<>();
queryMap.put("city","沈陽");
}
privateStringgetResponseString(String
fileName)
{
returnFileUtil.readFile(jsonFullPath+
fileName,"UTF-8").toString();
}
private
voidsetFinalStatic(Field
field,Object
newValue)throwsException
{
field.setAccessible(true);
Field
modifiersField
=
Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setint(field,field.getModifiers()
&
~Modifier.FINAL);
}
}首先通過@Mock注解對需要mock的對象進行初始化,然后我們需要對測試類進行測試case分析,WeatherModelmode類是一個網(wǎng)絡(luò)請求數(shù)據(jù)model,所以這個model類的核心是request函數(shù)。首先對request函數(shù)進行分析。必須涵蓋的測試點如下:請求參數(shù)校驗,請求成功且返回碼正確處理邏輯校驗,請求成功但校驗碼錯誤處理邏輯校驗和請求失敗處理邏輯校驗。同時Model類中還有一個觀察者解綁函數(shù),所以測試case也需要包含解綁函數(shù)處理邏輯測試這一項。通過initResponse,我們可以對接口返回值進行模擬,這里采用讀Json文件的方法將接口返回做成Json數(shù)據(jù)文件,結(jié)合服務(wù)端的Swagger文檔可以很輕易的實現(xiàn)服務(wù)端接口數(shù)據(jù)模擬。@Test@SuppressWarnings("unchecked")public
voidtestParams()
{
model.request(listener,"沈陽");
try{
Field
fieldParam
=
WeatherModel.class.getDeclaredField("queryMap");
Field
fieldKey
=
WeatherModel.class.getDeclaredField("CITY");
fieldParam.setAccessible(true);
setFinalStatic(fieldKey,
true);
Map
queryMaps
=
(Map)
fieldParam.get(model);
String
key
=
(String)
fieldKey.get(model);
assertEquals("驗證queryMap的Key",key,"city");
String
city
=
queryMaps.get("city");
assertEquals("驗證queryMap的value",city,"沈陽");
}
catch(Exception
e)
{
//reflect
error}
}對于有參數(shù)的Api,***步就是驗證傳參。可能你會覺得大材小用,但眾多的血淋淋的慘案告訴我們越是細小的東西越容易產(chǎn)生問題,而單元測試就是幫助我們將細小的問題解決在編碼時期而不對外暴露。要驗證參數(shù)的正確性,首先我們需要要驗證向queryMap中put的時候是否正確。對于queryMap,我們需要驗證K-V鍵值對的正確性,還是那句防微杜漸,因為queryMap是一個private變量,在正常情況下我們無法獲取到它的值,而為這個變量加一個對業(yè)務(wù)毫無用處的get/set方法就顯得太刻意了,我們的目的是為了解決讓代碼更健壯,bug更少,而不是為了測試而測試。拿不到queryMap參數(shù)測試還怎么進行?難道單元測試也要從入門到放棄?要其實很多事情都是這樣,當(dāng)你覺得某個問題完全沒有辦法解決的時候,一定是你考慮的不夠周全。queryMap對象的值我們可以通過Java反射獲得。反射的原理在這里我就不為大家闡述了,在testParams方法中,我們首先通過getDeclaredField獲取了queryMap對象,然后我們需要獲得到put的key。key的獲得使我們陷入了第二個難題,可能你會說,這有什么難的,繼續(xù)反射啊,可這個key是一個private
static變量,通過正常的反射是無法拿到key的,最多會拿到一個異常。還是那句,不要放棄尋找解決方案,最終我們發(fā)現(xiàn)只要設(shè)置下虛擬機不去檢測私有屬性,即可完成對private
static變量的獲取。不要覺得只是很小的一個參數(shù),這么勞師動眾不值得,據(jù)不完全統(tǒng)計,每天因為接口key值多寫或是寫錯一個字母而產(chǎn)生的bug不計其數(shù)。@Test@SuppressWarnings("unchecked")public
voidtestRequestSuccess()
{
initResponse();
Mockito.when(api.getWeather(queryMap)).thenReturn(Observable.just(netData));
ArgumentCaptor
captor
=
ArgumentCaptor.forClass(WeatherData.class);
model.request(listener,"沈陽");
Mockito.verify(api).getWeather(queryMap);
Mockito.verify(listener).showLoading();
Mockito.verify(listener).hideLoading();
Mockito.verify(convertData).convertData(captor.capture());
WeatherData
result
=
captor.getValue();
intstatus
=
result.getStatus();
assertEquals("驗證code",status,1000);
}保證的參數(shù)傳遞的前提下,我們接下來需要對接口返回狀態(tài)進行測試,首先便是成功態(tài)的接口返回。Mockito.when的作用是設(shè)定預(yù)期返回結(jié)果,例如case
testRequestSuccess()所要測試的是請求成功且返回碼正確的情況,所以我們對response的預(yù)期就是讓它執(zhí)行onNext方法,同時返回我們初始化好的完全正確的接口數(shù)據(jù)。Mockito.when使得測試代碼可以完全按照我們所預(yù)期的執(zhí)行。不過這個聲明必須在方法執(zhí)行之前,即Mockito.when必須比model.request(listener,"沈陽");先執(zhí)行才會生效。Junit提供了豐富的assert斷言機制,借助assert我們可以實現(xiàn)多種情況的測試,然而對于沒有明確返回值的void方法,assert就顯得有些無能為力,因為它無法找到一個標準進行斷言。這時候需要使用mockito的verify方法,它的作用是驗證mock對象的某一個方法是否得到了正確的執(zhí)行Mockito.verify(listener).showLoading();就是驗證加載進度條是否能夠正常顯示,ArgumentCaptor是一個參數(shù)捕獲,它可以捕獲onNext返回的數(shù)據(jù),通過assert斷言,我們可以驗證成功情況下數(shù)據(jù)是否正確。數(shù)據(jù)成功情況下,我們有一個網(wǎng)絡(luò)數(shù)據(jù)向視圖數(shù)據(jù)轉(zhuǎn)換的過程,這個轉(zhuǎn)換方法是在convert類中執(zhí)行的操作,因為我們做的是單元測試而非集成測試,所以基于WeatherModel這個測試類,我們只需要驗證到convertData()這個函數(shù)是否正確得到了調(diào)用即可,數(shù)據(jù)轉(zhuǎn)換的內(nèi)容由Convert類的單元測試進行跟蹤即可。@Testpublic
voidtestStatusError()
{
initResponse();
netData.setStatus(1001);
Mockito.when(api.getWeather(queryMap)).thenReturn(Observable.just(netData));
ArgumentCaptor
captor
=
ArgumentCaptor.forClass(WeatherData.class);
model.request(listener,"沈陽");
Mockito.verify(api).getWeather(queryMap);
Mockito.verify(listener).showLoading();
Mockito.verify(listener).fail(null,ServerCode.get(netData.getStatus()).getMessage());
}在實際開發(fā)過程中,服務(wù)端通常會對同一接口的不同狀態(tài)做成不同的服務(wù)應(yīng)答碼,雖然返回非常態(tài)應(yīng)答碼的時候網(wǎng)絡(luò)請求也是成功,但它卻是有別于常態(tài)服務(wù)端應(yīng)答的另一種情況。所以,這里需要對非常態(tài)服務(wù)應(yīng)答碼進行一個條件分支的測試。testStatusError
()的測試方法與testRequestSuccess()類似,只是我們這次的status模擬值由成功的status換成了一個異常status,同時,驗證的函數(shù)執(zhí)行也變成了listener的失敗方法@Testpublic
voidtestRequestFail()
{
initResponse();
Exception
exception
=newException("exception");
Mockito.when(api.getWeather(queryMap)).thenReturn(Observable.error(exception));
model.request(listener,"沈陽");
Mockito.verify(listener).fail(null,"exception");
}Request是一個接口,我們不能夠保證每次請求我們的服務(wù)器都能夠給與準確應(yīng)答,同時用戶在發(fā)出請求的時候我們也不能夠保證用戶所處的網(wǎng)絡(luò)狀態(tài)是否通暢。所以我們在設(shè)計Model類的時候也要將非常態(tài)考慮在內(nèi),對接口的異常情況進行處理,有時候我們需要自己創(chuàng)造一些異常來驗證我們代碼的健壯程度。同樣的,我們的測試類也需要有一個專門的方法來保證異常態(tài)的測試。testRequestFail()的測試方法與成功的方法的不同之處在于我們首先我們需要mock的不是接口數(shù)據(jù),而是一個異常,Exception
exception=new
Exception("exception");注意,這個Exception中的參數(shù)即是異常信息,因為我們的fail方法中有異常信息的顯示,所以這個參數(shù)是必須要加上的,否則e.getLocalizedMessage()會拋出NPE。另外,這個時候的Mockito.when的期望也有所改變,這次我們期望的是函數(shù)執(zhí)行onError方法。@Testpublic
voidtestCancelRequest()
{
Subscription
subscription
=mock(Subscription.class);
model.setSubscription(subscription);
model.cancelRequest();
verify(subscription).unsubscribe();
}Model類中***一個case是testCancelRequest()它的作用是,在合適的時候解綁request,我們的網(wǎng)絡(luò)請求是異步的,也就是說當(dāng)我們調(diào)用請求的activity或是fragment
destroy的時候,如果我們沒有解除綁定,是存在內(nèi)存泄漏風(fēng)險的。當(dāng)然,我們能想到的問題,Rxjava的維護者們也一定想到了,Subscription就是方便我們在生命周期結(jié)束的時候?qū)x解綁。驗證方法很簡單,還是通過verify方法,驗證解綁方法是否得到了正確執(zhí)行。dependencies
{
classpath'com.vanniktech:gradle-android-junit-jacoco-plugin:0.6.0'
}至此我們已經(jīng)完成了對model的全覆蓋測試,點擊測試類前面的運行按鈕,可以看到所有測試類運行的情況,綠色代表成功,紅色代表存在問題,可以通過下方的Log日志查看引起測試失敗的問題點進行改正,借助Jacoco統(tǒng)計工具可以看到單元測試覆蓋率的情況。之所以選擇使用Jacoco而不是IDE自帶的Coverage是因為在測試&條件分支的情況下Coverage存在漏洞,導(dǎo)致沒有達到全覆蓋的測試顯示已覆蓋完全。Jacoco的AndroidStudio集成網(wǎng)絡(luò)資源并不多,集成方法不是存在潛在漏洞就是過于繁瑣。經(jīng)過兩天的不斷搜索,終于發(fā)現(xiàn)了一個史上最簡單集成方法,只需要在主工程的gradle文件中添加一個Jacoco插件,gradle就會生成一個Jacoco
Task,雙擊運行即可生成一份Html覆蓋率報告。運行我們的model測試類,從jacoco生成的html可以看到,我們的model已經(jīng)達到了100%的全覆蓋。既然如此,我們是不是就可以認為MVP的M層已經(jīng)ok了呢?等等,我們好像遺漏了點什么,沒錯,onNext情況下的數(shù)據(jù)轉(zhuǎn)換類還沒有測試,下面我們來對convert類進行一下測試。首先們來看看convert類代碼:/**
*
Author
:
YangHaoyi
on
2017/6/28.
*
:
yanghaoyi@
*
Description
:網(wǎng)絡(luò)數(shù)據(jù)與視圖數(shù)據(jù)轉(zhuǎn)換器
*
Change
:
YangHaoYi
on
2017/6/28.
*
Version
:
V
1.0
*/
open
classWeatherDataConvert
{
open
funconvertData(netData:
WeatherData):WeatherViewData{
valviewData=
WeatherViewData()
viewData.temperature=
netData.data?.temperature?:0.0viewData.weatherType=
netData.data?.weatherType?:1viewData.ultraviolet=
netData.data?.ultraviolet?:0viewData.rainfall=
netData.data?.rainfall?:"0"viewData.hourTemperature=
netData.data?.hourTemperature?:"10"viewData.windPower=
netData.data?.windPower?:"2"returnviewData
}
}從代碼可以看出我們的convert類看起來有一些的奇怪,每錯,因為它并不是java代碼,它是kotlin。好好的java工程為什么要混入kotlin,單單只是為了炫技么?當(dāng)然不是,數(shù)據(jù)轉(zhuǎn)換類的作用是對網(wǎng)絡(luò)數(shù)據(jù)進行判空并包裝成視圖數(shù)據(jù),我們都知道在java中的判空,需要層層嵌套,例如,我們需要判斷Student類中的Score類中的EnglishScore字段,我們的寫法如下:if(Student!=null&&Student.getScore()!=null&&Student.getScore().getEnglishScore()!=null){}這是一個很多層的判斷,而對于kotlin我們只需要寫Student?.score?.englishScore即可,代碼量巨減有沒有。對于kotlin的特性,有興趣的同學(xué)可以移步官網(wǎng)去詳細了解。讓我們回歸單元測試,convert類是一個數(shù)據(jù)判空類,它的作用是對數(shù)據(jù)進行組裝并賦予默認初值,因為服務(wù)端的數(shù)據(jù)不可控,作為手機端我們不能把用戶體驗完全寄托于后端的兄弟,因為放過任何一個null數(shù)據(jù)對于App都是一個Crash。所以我們的測試點就是,這個類是否達到了當(dāng)數(shù)據(jù)為空的時候賦予默認值,當(dāng)數(shù)據(jù)不為空的時候取網(wǎng)絡(luò)數(shù)據(jù)值的作用。這里選取一個比較有代表性的testTemperature為例,首先設(shè)定模擬WeatherData的值為10D,因為網(wǎng)絡(luò)數(shù)據(jù)有值,所以會取網(wǎng)絡(luò)數(shù)據(jù)的值即10D,通過assertEquals可以進行斷言比對驗證,不過有一個需要注意的是double型的斷言assertEquals(message,double1,double2)是不可用的,直接運行的話會報測試失敗。Double的比對需要加上一個誤差值,這里給一個誤差值0.1D,再次運行,測試條變綠。同時我們需要測試當(dāng)WeatherData為空的情況下,viewData是否被賦予了默認值0.0。以此類推,我們需要對每一條數(shù)據(jù)進行校驗,并包裝成視圖數(shù)據(jù)。/**
*
Author
:
YangHaoyi
on
2017/7/7.
*
:
yanghaoyi@
*
Description
:
*
Change
:
YangHaoYi
on
2017/7/7.
*
Version
:
V
1.0
*/
public
classWeatherDataConvertTest
{
privateWeatherDataConvertconvert;
private
static
doubleDETAL=0.1D;
@Beforepublic
voidsetUp(){
convert=newWeatherDataConvert();
}
@Testpublic
voidtestTemperature(){
WeatherData
netData
=newWeatherData();
WeatherData.DataBean
dataBean
=newWeatherData.DataBean();
dataBean.setTemperature(10D);
netData.setData(dataBean);
WeatherViewData
viewData
=convert.convertData(netData);
//斷言double不可以用assertEquals(message,double1,double2)//需要改用下面的方法,DETAL為誤差值assertEquals(viewData.getTemperature(),10D,DETAL);
}
@Testpublic
voidtestTemperatureNull(){
WeatherData
netData
=newWeatherData();
WeatherData.DataBean
dataBean
=newWeatherData.DataBean();
netData.setData(dataBean);
WeatherViewData
viewData
=convert.convertData(netData);
//斷言double不可以用assertEquals(message,double1,double2)//需要改用下面的方法,DETAL為誤差值assertEquals(viewData.getTemperature(),0D,DETAL);
}
}Convert類的順利執(zhí)行標志著Model層的測試圓滿結(jié)束,下面讓我們來看一看MVP架構(gòu)下的第二順位View層的測試,如果我們不借助UI測試框架直接運行UI測試是無法得到預(yù)期的驗證的,因為我們只會得到一個運行時異常。可是我們在構(gòu)建工程之前已經(jīng)下載了對應(yīng)版本的安卓Sdk,為什么還是會拋出異常呢?在真機或是模擬器上面為什么不會呢?是不是IDE只為我們提供了工程的開發(fā)與編譯環(huán)境,并沒有提供工程的運行環(huán)境呢?引用Linus
Torvalds的那句經(jīng)典的RTFSC,讓我們通過源碼來一點點驗證我們的猜想。首先我們找到SDK對應(yīng)的android.jar文件,然后隨便找個工程addas
library,以我們最常用的Activity為例,源碼如下:public
WindowManager
getWindowManager()
{
throw
newRuntimeException("Stub!");
}
public
Window
getWindow()
{
throw
newRuntimeException("Stub!");
}
public
LoaderManager
getLoaderManager()
{
throw
newRuntimeException("Stub!");
}
public
View
getCurrentFocus()
{
throw
newRuntimeException("Stub!");
}
protected
void
onCreate(BundlesavedInstanceState)
{
throw
new
RuntimeException("Stub!");
}
public
void
onCreate(BundlesavedInstanceState,
PersistableBundle
persistentState)
{
throw
newRuntimeException("Stub!");
}我們可以清除的看到所有的方法都不約而同的拋出了RuntimeException("Stub!"),這也就是我們的測試case無法進行的原因。為了應(yīng)對UI單元測試難以推進的現(xiàn)狀,谷歌推出了一套名為Espresso的UI單元測試框架,由于是官方的框架,所以在工程的運行以及相關(guān)資料的跟進都做的比較完善。然而Espresso的短板也非常明顯,Espresso必須借助于安卓模擬器或是真機環(huán)境才能夠運行,也正是因為需要在安卓設(shè)備上運行,Espresso的運行速度非常緩慢,使之與Jenkins相結(jié)合進行自動化構(gòu)建更是難上加難。這不禁讓我陷入沉思,如果UI單元測試需要如此的大費周章,那是否還有測下去的必要?不過很快迭代的bug統(tǒng)計就打消了我放棄UI只做邏輯測試的念頭。我們手機組在迭代過程中的UI與邏輯bug比基本可以達到5比1,也就是說有絕大多數(shù)問題產(chǎn)生在了視圖層,單元測試的目的是減少bug產(chǎn)生,而目前UI就是我們***的痛點,UI單元測試勢在必行。經(jīng)過不斷的資源搜索,最終我到了一個可以不借助安卓設(shè)備的UI測試框架Robolectric,它的設(shè)計思路是通過實現(xiàn)一套JVM能運行Android代碼,從而做到脫離Android環(huán)境進行測試。由于robolectric需要從下載一些必要的依賴包,但是是國外的網(wǎng)站,下載速度比較緩慢。這里需要修改整個工程的build.gradle文件,修改mavenCentral()為阿里云{"/nexus/content/groups/public/"}
的代理。Robolectric的依賴為:testCompile'org.robolectric:robolectric:3.3.2'運行Robolectric需要首先對測試類進行配置,如下:@RunWith(MyRobolectricTestRunner.class)@Config(constants=
BuildConfig.class,sdk=24)MyRobolectricTestRunner為自定義的指向阿里云的配置文件,BuildConfig為當(dāng)前model的BuildConfig文件,sdk為使用的sdk版本,之所以指定sdk版本是因為Robolectric需要下載對應(yīng)sdk的鏡像資源,指定版本就會使用本地已經(jīng)下載好的sdk資源。***次運行測試的時候會自動到阿里云去下載相關(guān)文件,然后會在系統(tǒng)的C盤下生成一個.m2文件夾,如果依舊下載緩慢,可直接拷貝.m2文件夾到自己電腦的相對目錄下直接使用。Robolectric幾乎可以測試一切安卓方法,使用也是非常簡單。例如:@Beforepublic
voidsetUp()
{
activity=
Robolectric.setupActivity(WeatherActivity.class);
}實現(xiàn)的便是創(chuàng)建一個Activity,一行代碼即可模擬activity的創(chuàng)建與運行。一行代碼就解決了一直困擾我們對于android環(huán)境無法獲取的苦惱。有了Activity對象,瞬間覺得可以解決所有問題。例如測試頁面的跳轉(zhuǎn):@Testpublic
voidtestToHelpCenter(){
view.toHelpCenter();
//設(shè)置期待IntentIntent
expectedIntent
=newIntent(activity,WeatherHelpCenterActivity.class);//獲取實際IntentIntent
actualIntent
=
ShadowApplication.getInstance().getNextStartedActivity();//通過Assert驗證Assert.assertEquals(expectedIntent.getComponent(),actualIntent.getComponent());
}設(shè)置好當(dāng)前頁面與跳轉(zhuǎn)頁面,Robolectric就能夠幫助我們模擬出我們所期待的Intent,同時通過ShadowApplicaiton可以獲取到模擬運行后的實際Intent的值,結(jié)合Junit即可完成對Intent的驗證,進而驗證頁面跳轉(zhuǎn)邏輯。TextView是我們在開發(fā)過程中最常用也是最容易出錯的一個UI組件,尤其是團隊的設(shè)計師是一個非常把不同地方的文案設(shè)計得非常想象而又有著細微差別的時候,我們非常容易多打或是少打一個字,又或是錯別或是形近字。為了保證產(chǎn)品質(zhì)量,我們不得不一遍又一遍的比對UI稿件,錙銖必較,逐字觀察,簡直苦不堪言。所謂程序即生活,難道我們生活中就沒有這種校驗文字的困擾么?生活中我們又都是怎么解決的呢?記得許多年前時不時會看到有人去ATM轉(zhuǎn)賬轉(zhuǎn)錯的新聞,今年來倒是很少有這樣的新聞了,原因就在于銀行對于銀行卡號作了二次校驗。對于TextView的測試也是利用了二次校驗的方法,***次文字使用業(yè)務(wù)代碼,第二次代碼使用測試代碼進行校驗,如果兩次不一致則證明文字存在問題。這樣就可以有效的避免了靠肉眼比對的不確定性,讓程序去驗證程序。@Testpublic
voidtestShowTemperature(){
//模擬視圖數(shù)據(jù)WeatherViewData
viewData
=newWeatherViewData();
viewData.setTemperature(23.1D);
view.updateCache(viewData);
//執(zhí)行待測函數(shù)view.showTemperature();//通過Id獲得view實體TextView
tvTemperature
=
(TextView)activity.findViewById(R.id.tvTemperature);
String
text
=
tvTemperature.getText().toString();
//驗證文字顯示assertEquals("驗證溫度",text,"23.1");
}首先通過view.showTemperature();調(diào)用執(zhí)行函數(shù),在通過Id找到對應(yīng)的TextView組件,通過getText獲取TextView的顯示文字,再通過Junit的aseertEquals進行字符串驗證即可。如果發(fā)生比對失敗,通過下方的Log提示click
toseedifference即可準確的看到差異點。Robolectric對于提示Tost的測試也是非常的簡單,只需要:@Testpublic
voidtestShowDataError(){
view.showDataError();
assertEquals("數(shù)據(jù)轉(zhuǎn)換異常",ShadowToast.getTextOfLatestToast());
}測試Resource中的顏色:@Testpublic
voidtestInitTitle(){
TextView
tvTitle
=
(TextView)activity.findViewById(R.id.tvTitle);
view.initTitle();
String
title
=
tvTitle.getText().toString();
assertEquals("驗證標題初始化",title,"幫助中心");
Application
application
=
RuntimeEnvironment.application;
ColorStateList
color
=
ColorStateList.valueOf(application.getResources().getColor(R.color.colorWhite));
assertEquals("驗證顏色",color,tvTitle.getTextColors());
}測試Dialog:@Testpublic
voidtestShowTelDialog(){
view.showTelDialog();
//因為提示框
dialog
在
view
中屬于私有變量,不需要對外暴露方法,如果為了測試而寫一個get
set
方法似乎太過牽強//所以采用
Java
反射的方法獲取dialog對象try{//
/通過類的字節(jié)碼得到該類中聲明的所有屬性,無論私有或公有Field
field
=
WeatherHelpCenterImpl.class.getDeclaredField("telDialog");//
設(shè)置訪問權(quán)限(這點對于有過android開發(fā)經(jīng)驗的可以說很熟悉)field.setAccessible(true);//
得到私有的變量值Object
dialog
=
field.get(view);
TConfirmDialog
telDialog
=
(TConfirmDialog)
dialog;
//獲取到Dialog對象之后,再通過反射獲取Dialog中TextView對象Field
fieldDialog
=
TConfirmDialog.class.getDeclaredField("tvTitle");//
設(shè)置訪問權(quán)限fieldDialog.setAccessible(true);//獲取telDialog中的TextView對象Object
title
=
fieldDialog.get(telDialog);
TextView
tvTitle
=
(TextView)
title;
//通過assert方法驗證標題assertEquals("驗證標題",tvTitle.getText().toString(),"客服電話");//獲取到Dialog對象之后,再通過反射獲取Dialog中TextView對象fieldDialog
=
TConfirmDialog.class.getDeclaredField("tvConfirm");//獲取telDialog中的TextView對象Object
confirm
=
fieldDialog.get(telDialog);
TextView
tvConfirm
=
(TextView)
confirm;
//通過assert方法驗證標題assertEquals("驗證確定按鈕",tvConfirm.getText().toString(),"撥打電話");//獲取到Dialog對象之后,再通過反射獲取Dialog中TextView對象fieldDialog
=
TConfirmDialog.class.getDeclaredField("tvCancel");//獲取telDialog中的TextView對象Object
cancel
=
fieldDialog.get(telDialog);
TextView
tvCancel
=
(TextView)
cancel;
//通過assert方法驗證標題assertEquals("驗證取消按鈕",tvCancel.getText().toString(),"取消");
}
catch(Exception
e)
{
//error}
}Dialog的測試點需要包括Dialog的顯示與隱藏,Dialog的提示文字與按鈕的文字顯示,因為很多是私有變量,所以這里用到了一些Java反射來幫助獲取對象。目前為止,我們已經(jīng)完成了對Model層與View層的測試,MVP三兄弟只剩下P層還沒有測試,下面我們就來看看P層該如何測試。P層作為M層與V層的紐帶,起到了隔離視圖與數(shù)據(jù)直接交互的作用。因為P層持有的只是V的接口,所以P層也可以抽離成簡單的純Java測試。讓我們先來看看P層的測試代碼:/**
*
Created
by
YangHaoyi
on
2017/7/8.
*
:
yanghaoyi@
*
Description
:
*
Version
:
*/
public
classWeatherPresenterTest
{
priva
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度軟裝行業(yè)展會組織與推廣合同
- 小學(xué)家委主任發(fā)言稿
- 閉門溝通發(fā)言稿
- 2025年新疆道路運輸從業(yè)資格證考試內(nèi)容是什么
- 高中家長會:高三上學(xué)期家長會課件
- 內(nèi)墻乳膠漆粉刷合同
- 2024年標準離婚協(xié)議
- 高中家長會 有效陪伴有力助學(xué)課件-高中暑期家長會
- 采購訂單狀態(tài)更新表
- 環(huán)境監(jiān)測與控制表格
- 2025年全國國家版圖知識競賽題庫及答案(中小學(xué)組)
- 2025年合肥職業(yè)技術(shù)學(xué)院單招職業(yè)適應(yīng)性測試題庫完整版
- 2025年黑龍江旅游職業(yè)技術(shù)學(xué)院單招職業(yè)傾向性測試題庫匯編
- 2025年湖南城建職業(yè)技術(shù)學(xué)院單招職業(yè)技能測試題庫新版
- 國家基本藥物臨床應(yīng)用指南
- 2025春-新版一年級語文下冊生字表(200個)
- 企業(yè)級軟件開發(fā)作業(yè)指導(dǎo)書
- 護士法律法規(guī)知識培訓(xùn)
- 《中國古代文學(xué)史及作品選II》教學(xué)大綱
- 代工生產(chǎn)合同范本
- 人教版英語2025七年級下冊 Unit1Animal Friends教師版 語法講解+練習(xí)
評論
0/150
提交評論