簡(jiǎn)單談?wù)刟ndroid studio 的單元測(cè)試
面對(duì)android studio Run 一次項(xiàng)目要等好幾分鐘的痛點(diǎn),不得不研究一下android studio 的單元測(cè)試。
其實(shí)我的目的很簡(jiǎn)單,在不對(duì)視圖進(jìn)行操作的前提下,測(cè)試一些activity 的生命周期,或網(wǎng)絡(luò)拉取數(shù)據(jù)的一些處理,比如解析 json 數(shù)據(jù)啊,做網(wǎng)絡(luò)請(qǐng)求啊等等,也就是對(duì) Model層的測(cè)試。這些不需要操作視圖,但在沒(méi)有單元測(cè)試環(huán)境下,比如我們網(wǎng)絡(luò)請(qǐng)求一些數(shù)據(jù),Log 打印看看是否請(qǐng)求成功,卻又要 利用模擬器或真機(jī)Run 一次項(xiàng)目,花費(fèi)好幾分鐘,這是不能容忍的。
于是乎,強(qiáng)大的 android studio 也考慮到了這一點(diǎn),給我們提供的簡(jiǎn)單的單元測(cè)試類(lèi)。
讓我們來(lái)簡(jiǎn)單的了解學(xué)習(xí)一下吧。
首先先來(lái)了解一下一些名稱,方便下面介紹和使用:
在java中咱們有用過(guò) JUnit 的 單元測(cè)試 ,那android 也是基于 java 語(yǔ)言編寫(xiě)的,所以也有個(gè) JUnit的單元測(cè)試。在做 android 的單元測(cè)試需要導(dǎo)入依賴:
androidTestCompile 'junit:junit:4.12'
testCompile 'junit:junit:4.12'
其中, test目錄為在本機(jī)執(zhí)行單元測(cè)試代碼的目錄, androidTest為在Android設(shè)備上執(zhí)行單元測(cè)試代碼的目錄。如下圖:

Android 自帶的 junit單元測(cè)試的一些測(cè)試類(lèi)(androidTest測(cè)試 需要運(yùn)行在模擬機(jī)或真機(jī)上)
1、InstrumentationTestCase框架:
Instrumentation和Activity有點(diǎn)類(lèi)似,只不過(guò)Activity是需要一個(gè)界面的,而Instrumentation并不是這樣的,我們可以將它理解為一種沒(méi)有圖形界面的,具有啟動(dòng)能力的,用于監(jiān)控其他類(lèi)(用Target Package聲明)的工具類(lèi)。
舉個(gè)例子,利用InstrumentationTestCase 啟動(dòng)一個(gè)activity:
在androidTest下新建一個(gè)java類(lèi),并且繼承自InstrumentationTestCase編寫(xiě)一個(gè)public void的方法,但是必須要是方法名以test打頭,比如testPublishSubject,并不需要@Test注解
public class TestSubject extends InstrumentationTestCase {
private static final String LOG_TAG = "test";
public void testPublishSubject() {
launchActivity("demo.zts.com.demo",SecondActivity.class,null);
}
}
2、ApplicationTestCase——測(cè)試整個(gè)應(yīng)用程序的類(lèi)。它允許你注入一個(gè)模擬的Context到應(yīng)用程序中,在應(yīng)用程序啟動(dòng)之前初始化測(cè)試參數(shù),并在應(yīng)用程序結(jié)束之后銷(xiāo)毀之前檢查應(yīng)用程序。
使用Context,你可以瀏覽資源,文件,數(shù)據(jù)庫(kù)等等?;?lèi)是AndroidTestCase,一般常見(jiàn)的是它的子類(lèi),和特定組件關(guān)聯(lián)。
測(cè)試代碼如下:
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
String app_name = getResources().getString(R.string.app_name);
Log.i("MyApp",".........MyApp....app_name.........."+app_name);
}
}
public class ApplicationTest extends ApplicationTestCase<MyApp> {
public ApplicationTest() {
super(MyApp.class);
}
public void testStart() {
String str = null;
str = mContext.getResources().getString(R.string.app_name);
Log.i("..",".............ApplicationTest ...........app_name............."+str);
}
Log 日志:
07-22 23:27:10.276 32259-32259/demo.zts.com.demo I/MyApp: .........MyApp....app_name..........demo
07-22 23:27:10.276 32259-32319/demo.zts.com.demo I/TestRunner: started: testStart(demo.zts.com.demo.ApplicationTest)
07-22 23:27:10.286 32259-32319/demo.zts.com.demo I/..: .............ApplicationTest..........app_name..............demo
3、ActivityUnitTestCase——對(duì)單個(gè)Activity進(jìn)行單一測(cè)試的類(lèi)。使用它,你可以注入模擬的Context或Application,或者兩者。它用于對(duì)Activity進(jìn)行單元測(cè)試。也就是說(shuō)你可以用于測(cè)試單獨(dú)的activity ,雖然也需要利用模擬機(jī)或真機(jī)啟動(dòng),但你啟動(dòng)的只是你需要做測(cè)試的activity,于其他activity無(wú)關(guān)。
測(cè)試代碼如下:
要測(cè)試的 activity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("...............MainActivity......onCreate............");
Log.i("MainActivity","................onCreate............................");
}
@Override
protected void onStart() {
super.onStart();
System.out.println("...............MainActivity......onStart............");
Log.i("MainActivity","................onStart............................");
}
@Override
protected void onStop() {
super.onStop();
System.out.println("...............MainActivity......onStop............");
Log.i("MainActivity","................onStop............................");
}
@Override
protected void onDestroy() {
super.onDestroy();
System.out.println("...............MainActivity......onDestroy............");
Log.i("MainActivity","................onDestroy............................");
}
}
測(cè)試類(lèi)
public class TestActivity extends ActivityInstrumentationTestCase2<MainActivity> {
private Context ctx;
public TestActivity() {
super(MainActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
ctx = getActivity().getApplicationContext();
}
public void testStart() {
Intent intent = new Intent(ctx, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startActivity(intent);
Log.i("TestActivity","................startActivity............................");
}
測(cè)試 Log 日志:
* 07-22 23:39:44.146 3171-3171/demo.zts.com.demo I/System.out: ...............MainActivity......onCreate............
07-22 23:39:44.146 3171-3171/demo.zts.com.demo I/MainActivity: ................onCreate............................
07-22 23:39:44.151 3171-3171/demo.zts.com.demo D/MZPerfObserver: demo.zts.com.demo onCreate consume 153 ms
07-22 23:39:44.151 3171-3171/demo.zts.com.demo I/System.out: ...............MainActivity......onStart............
07-22 23:39:44.151 3171-3171/demo.zts.com.demo I/MainActivity: ................onStart............................
07-22 23:39:44.326 3171-3171/demo.zts.com.demo D/OpenGLRenderer: Enabling debug mode 0
07-22 23:39:44.361 3171-3171/demo.zts.com.demo I/System.out: ...............MainActivity......onStop............
07-22 23:39:44.361 3171-3171/demo.zts.com.demo I/MainActivity: ................onStop............................
07-22 23:39:44.421 3171-3224/demo.zts.com.demo I/TestActivity: ................startActivity............................
還有很多常見(jiàn)的測(cè)試,比如ServiceTestCase,ProviderTestCase2等,大家需要慢慢琢磨。
Android 自帶的 junit單元測(cè)試的一些測(cè)試類(lèi)(test 測(cè)試 ,不需要模擬機(jī),電腦直接運(yùn)行)
比如我需要測(cè)試一段java代碼,而這段java代碼跟android沒(méi)關(guān)系,也就是不用到android的資源,如context,activity 等,說(shuō)白了就是簡(jiǎn)單的 java 測(cè)試,當(dāng)然,嘿嘿,android studio也是可以做java代碼測(cè)試的。
測(cè)試代碼如下,測(cè)試 4+4 等于幾:
public class ExampleUnitTest {
@Test
public void testAdd() {
int i = 0;
i = 4+4;
System.out.print(".............. "+i);
Log.i("TAG","..................."+i);
// 比較 i 是否 等于 8 ,相等的話通過(guò)測(cè)試?。?!
Assert.assertEquals(8, i);
}
}
測(cè)試成功:

以上測(cè)試類(lèi)的運(yùn)行是 -點(diǎn)擊測(cè)試右鍵 - 選擇 RunXXXXX

/*********************華麗分割線***********************/
看了半天好像也沒(méi)有解決文章最初提到的一個(gè)痛點(diǎn)啊,就是我需要測(cè)試android的資源,但又不想運(yùn)行笨重的模擬機(jī)或真機(jī),怎么辦呢? 媽蛋,被騙了,還錢(qián) -_-、、、 確實(shí),上面提到的測(cè)試方法雖然沒(méi)有解決拜托模擬機(jī)測(cè)試的痛點(diǎn),但基于模擬機(jī)單元測(cè)試的 androidTest 確實(shí)方便我們做一些 單獨(dú)功能的測(cè)試,而且能做 UI 測(cè)試,因?yàn)樾枰M機(jī)或真機(jī)嘛,所以 UI 或視圖測(cè)試是沒(méi)問(wèn)題的。 還有test 測(cè)試,可以做一些不需要android資源的 java代碼測(cè)試,也是在android開(kāi)發(fā)當(dāng)中很方便的,不用在啟用eclipse 做測(cè)試,直接android studio 既可以了。
忽悠,接著 忽悠 -_-////
其實(shí)要想脫離 模擬機(jī)或真機(jī),又要做使用android資源的測(cè)試,如 使用Context,瀏覽資源,文件,數(shù)據(jù)庫(kù)等等。 也是可以的!??! 那 就只有第三方測(cè)試框架了 Robolectric
666,你是來(lái)做宣傳的嗎 -_-、、、不過(guò)真的很好用,也能很好的解決咱們的痛點(diǎn)。
接下來(lái)利用個(gè)需求來(lái)講解 Robolectric 測(cè)試,免得我忽悠你們。
拿到 android 目錄下的 assets 下的json01.txt文件 是一段json數(shù)據(jù),讓后進(jìn)行解析,解析后將數(shù)據(jù)顯示。 分析:這個(gè)需求就跟android下的資源有關(guān),而咱們利用 Robolectric 做單元測(cè)試,并且不需要模擬機(jī)或真機(jī)的支持。

其中json數(shù)據(jù)
{
"name": "coolxing",
"age": 24,
"male": true,
"address": {
"street": "huiLongGuan",
"city": "beijing",
"country": "china"
}
}
首先需要 Robolectric 依賴,在你的 app module 下注入依賴:
testCompile 'org.robolectric:robolectric:3.0'
注意是 testCompile 而不是 androidTestCompile ,不然你有需要啟動(dòng)模擬器了。并且測(cè)試類(lèi)也是 在 test 下的

測(cè)試類(lèi):
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest2 {
@Test
public void testJson(){
String str = null;
str = RuntimeEnvironment.application.getResources().getString(R.string.app_name);
AssetManager am = null;
am = RuntimeEnvironment.application.getAssets();
String strData = null;
try {
InputStream inputStream = am.open("json01.txt");
byte buf[] = new byte[1024];
inputStream.read(buf);
strData = new String(buf);
strData =strData.trim();
strData.trim();
} catch (IOException e) {
}
jsonBean foo = new Gson().fromJson(strData, jsonBean.class);
System.out.println("...............json.................."+foo.name);
System.out.println("...............json.................."+foo.address);
System.out.println("...............json.................."+foo.age);
}
}
測(cè)試結(jié)果:

看,咱們利用application 拿到 android 下的資源,但又不像剛才上面的 androidTestCompile 需要模擬機(jī),是不是很6,我電腦配置比較低,本次測(cè)試需要40S多,但不真機(jī)快多了。
am = RuntimeEnvironment.application.getAssets();
需要注意幾點(diǎn),類(lèi)頭部需要聲明 @ 注解:
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
并且測(cè)試方法是以 textxxx() 開(kāi)頭的,如上面的 testJson() ,方法也需要@Test注解?。?!
Robolectric 還可以測(cè)試 activity ,如:
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class MainActivityTest2 {
@Test
public void testMainActivity() {
MainActivity mainActivity = Robolectric.setupActivity(MainActivity.class);
mainActivity.findViewById(R.id.main_tv).performClick();
Intent expectedIntent = new Intent(mainActivity, SecondActivity.class);
ShadowActivity openActivity = Shadows.shadowOf(mainActivity);
Intent actualIntent = openActivity .getNextStartedActivity();
// Assert.assertEquals(expectedIntent, actualIntent);
}
其中
MainActivity mainActivity = Robolectric.setupActivity(MainActivity.class);
這句代碼就是啟動(dòng)了MainActivity 的生命周期

Robolectric 單元測(cè)試類(lèi) 的 啟動(dòng) 也是跟 上面test 測(cè)試類(lèi)一樣,選擇 -MainActivityTest2 --右鍵 -- 選擇 Run MainActivityTest2
好了,單元測(cè)試就介紹到這里,
其實(shí)我也只是初步理解,上面那些基本的也是我做項(xiàng)目的需要我才去學(xué)習(xí)使用的,還有好多強(qiáng)大的功能大家慢慢探索。
- 詳解appium+python 啟動(dòng)一個(gè)app步驟
- Python腳本在Appium庫(kù)上對(duì)移動(dòng)應(yīng)用實(shí)現(xiàn)自動(dòng)化測(cè)試
- 詳解Android單元測(cè)試最佳實(shí)踐
- android開(kāi)機(jī)自啟動(dòng)APP及使用adb命令測(cè)試方法
- Android利用Espresso進(jìn)行UI自動(dòng)化測(cè)試的方法詳解
- 在Android打包中區(qū)分測(cè)試和正式環(huán)境淺析
- Android單元測(cè)試之對(duì)Activity的測(cè)試示例
- 淺談Android單元測(cè)試的作用以及簡(jiǎn)單示例
- Android和iOS 測(cè)試五個(gè)最好的開(kāi)源自動(dòng)化工具
- Android 中構(gòu)建快速可靠的 UI 測(cè)試
- Android Monkey壓力測(cè)試詳細(xì)介紹
- Ubuntu中為Android系統(tǒng)上實(shí)現(xiàn)內(nèi)置C可執(zhí)行程序測(cè)試Linux內(nèi)核驅(qū)動(dòng)程序
- Android App開(kāi)發(fā)的自動(dòng)化測(cè)試框架UI Automator使用教程
- Android自動(dòng)測(cè)試工具M(jìn)onkey的實(shí)現(xiàn)方法
- Android測(cè)試中Appium的一些錯(cuò)誤解決技巧
相關(guān)文章
Android 模擬器(JAVA)與C++ socket 通訊 分享
Android 模擬器(JAVA)與C++ socket 通訊 分享,需要的朋友可以參考一下2013-05-05
Android Jetpack架構(gòu)組件 ViewModel詳解
這篇文章主要介紹了Android Jetpack架構(gòu)組件 ViewModel詳解,ViewModel類(lèi)讓數(shù)據(jù)可在發(fā)生屏幕旋轉(zhuǎn)等配置更改后繼續(xù)存在,ViewModel類(lèi)旨在以注重生命周期的方式存儲(chǔ)和管理界面相關(guān)的數(shù)據(jù)。感興趣可以來(lái)學(xué)習(xí)一下2020-07-07
Android PopupWindow使用方法小結(jié)
這篇文章主要為大家詳細(xì)介紹了Android PopupWindow使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
Android中TextView和ImageView實(shí)現(xiàn)傾斜效果
這篇文章主要為大家詳細(xì)介紹了Android中TextView和ImageView實(shí)現(xiàn)傾斜效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Flutter實(shí)現(xiàn)仿微信分享功能的示例代碼
Flutter 用來(lái)快速開(kāi)發(fā) Android iOS平臺(tái)應(yīng)用,在Flutter 中,通過(guò) fluwx或者fluwx_no_pay 插件可以實(shí)現(xiàn)微信分享功能,本文將具體介紹實(shí)現(xiàn)的示例代碼,需要的可以參考一下2022-01-01
Android使用AlertDialog實(shí)現(xiàn)對(duì)話框
本文主要介紹了Android使用AlertDialog實(shí)現(xiàn)對(duì)話框的相關(guān)知識(shí),具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-03-03
Android離線Doc文檔訪問(wèn)速度慢的有效解決方法
今天小編就為大家分享一篇關(guān)于Android離線Doc文檔訪問(wèn)速度慢的有效解決方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
Android入門(mén)之計(jì)時(shí)器Chronometer的使用教程
Chronometer是一個(gè)簡(jiǎn)單的定時(shí)器,你可以給它一個(gè)開(kāi)始時(shí)間,并以此定時(shí)。本文將利用個(gè)簡(jiǎn)單的示例為大家講解一下它的使用,感興趣的小伙伴可以嘗試一下2022-11-11

