編輯:關於Android編程
且說正文之前,還是先說說Android單元測試的意義或者說為什麼我們要進行Android的單元測試?
單元測試可以幫助我們程序員將bug消滅在萌芽期,為後續的集成測試減少時間.大家可以看一下這篇文章,可能會說服你:為什麼要進行煩人的單元測試?
如果那篇文章還不能說服你,那我只能使用殺手锏了~~
大家可以去各大招聘網站上看看,大部分公司都要求開發人員會編寫測試用例或使用框架或工具進行測試,並且大公司要求更甚.
這下還有什麼好說的嗎?單元測試是一個硬要求,即便你不喜歡單元測試,但是如果你想進入一個理想的公司,這是必備的一個技能,所以無論如何你都要去學習的,反正技多不壓身嘛.
那麼,如果你想學習單元測試,從什麼地方找學習資源呢?
但不管是通過上面哪個途徑,主要說的都是通過Espresso框架進行單元測試.
當然也有其他的一些測試框架,大家也可以去嘗試,但是Espresso框架是google官方大力推薦的一套測試框架,所以無論如何都要學習一下的.另外,自Android Studio2.2版本開始,google就為Espresso框架內置了一個圖形化界面,用來自動生成單元測試代碼.在下一篇文章,我會介紹如何利用這種圖形化界面進行單元測試代碼的編寫.
下面正式開始
首先第一步要做的就是集成Espresso測試環境,非常簡單,在你要測試的Module的gradle裡添加如下兩個依賴:
testCompile 'junit:junit:4.12' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }
第一個是junit依賴,這個依賴Android Studio一般都會默認配置,所以你只需要配置第二個依賴即可.
@RunWith(AndroidJUnit4.class) @LargeTest public class MainActivityInstrumentationTest { @Rule public ActivityTestRule mActivityRule = new ActivityTestRule<>( MainActivity.class); @Test public void sayHello(){ onView(withText("Say hello!")).perform(click()); onView(withId(R.id.textView)).check(matches(withText("Hello, World!"))); } }
上面這段代碼是android官網上面的,關於Espresso的原理我就不說了,我也講不來,強行講的話,也講的太膚淺.想了解原理的可以去Google一把.
那麼我們首先關注兩個注解:
顧名思義,測試規則,官方的解釋是你可以在該注解下引用一個規則或定義一個方法,而你引用或定義的就是測試規則,這樣說可能不是很清楚,可以看該注解下面的一句話:
public ActivityTestRule mActivityRule = new ActivityTestRule<>( MainActivity.class);
這句話就定義了一個測試規則,可以看到構造方法的參數裡指定了一個 MainActivity.class,具體的體現就是當你運行這段測試代碼時,app將會直接打開 MainActivity界面然後進行你所定義的測試用例.所以當你想直接測試某個界面時,你可以把那個界面填到這個參數裡,這樣就直接打開你指定的界面進行測試了.
該注解用來定義一個測試用例,當你的測試類運行時,所執行的代碼就是Test注解下的(Espresso還提供了其他的一些注解,比如:@After,@Before等,具體的用法可以去我上面寫的android官網上查看),當然上面那段代碼對應的就是sayHello測試方法,sayHello方法裡所定義的就是要測試的內容,該內容的含義為:
onView(withText("Say hello!")).perform(click());
含義:根據文本”Say hello!”找個這個控件然後執行該控件的點擊方法.
onView(withId(R.id.textView)).check(matches(withText("Hello, World!")));
含義:根據id “textView”找到這個控件然後檢查該控件上面顯示的文本是不是”Hello, World!”.
是不是很容易理解?非常語義化,一般都能看的懂.Espresso還提供了其他的方法供我們測試時調用,下面會列舉一些常用的.
但是在列舉常用的方法之前,需要先說明幾點Espresso的注意事項,不然當你測試的時候會因為Espresso報的各種錯誤氣個半死!!!
無論是通過withId()找控件還是通過withText()找控件或者其他方式比如withClassName(),withResourceName(),withTagKey()等方法,都要一定保證你所找的控件在當前頁面確實存在且可見,不然會報:NoMatchingViewException,當然你可能還會碰到其他異常,比如AmbiguousViewMatcherException,AppNotIdleException異常等等,具體的報錯原因可以到android官網查看,地址如下:Espresso的各種異常 如果你要測試AdapterView ,比如 ListView 或GridView等,使用上面的onView()方法是無效的,因為AdapterView的布局item是動態呈現的,沒法直接指定,所以當你要測試AdapterView時,請把onView()方法換成onData() 方法,與onView()方法返回ViewInteraction類似,onData()方法返回DataInteraction,二者用法基本都是一樣的.在上面的一段官網代碼中,我們用到了perform(click()),那麼除了click()方法還有其他功能強大的方法可以供我們使用,下面列舉一些常用的方法:
click():除了以上的常用方法還有其他一些不常用的,想繼續研究的可以查看Espresso中的ViewActions類,需要注意的是,所有的方法包括上面說到的和沒說到的,都有一個必須的前提條件,就是你要執行的view必須在當前界面上顯示出來,這有兩層意思:
1,當前界面必須能找到這個控件
2,這個控件必須是可見的
這是以上所有方法中通用的要求,當然有些方法還有額外的要求,比如必須要先獲取焦點等,
說到這裡,我們梳理一下我們學習了哪些東西:
1,如何根據id或文本或其他方法找到具體控件
2,如何讓這個控件執行相關操作
3,一些使用注意事項
所以,現在我們完全可以自己寫一個完整的測試用例了.
先來個gif圖:
上面的動圖測試的流程為:打開軟件,滑動Viewpager的3個頁面,在最後一個頁面點擊開始體驗按鈕進入主界面,點擊預約叫車,由於沒有登錄所以會跳轉到登陸頁面,輸入手機號,然後點擊獲取驗證碼按鈕,然後輸入驗證碼,最後點擊登陸.
整個流程對應的代碼測試代碼如下:
@LargeTest @RunWith(AndroidJUnit4.class) public class StartActivityTest { @Rule public ActivityTestRulemActivityTestRule = new ActivityTestRule<>(StartActivity.class); @Test public void startActivityTest() { //根據id找到ViewPager頁面,並判斷是否可見 ViewInteraction appCompatViewPager = onView( allOf(withId(R.id.viewPager), isDisplayed())); // 向左滑動viewpager頁面,下面3句也可以寫成一句話,Espresso會從左到右依次執行 // appCompatViewPager.perform(swipeLeft(),swipeLeft(),swipeLeft()); appCompatViewPager.perform(swipeLeft()); appCompatViewPager.perform(swipeLeft()); appCompatViewPager.perform(swipeLeft()); //根據文本找到"開始體驗"按鈕,並判斷是否可見 ViewInteraction appCompatButton = onView( allOf(withText("開始體驗"), isDisplayed())); //執行按鈕的點擊操作 appCompatButton.perform(click()); //根據控件的id和該控件的父布局id找到控件,並判斷是否可見 ViewInteraction appCompatImageView = onView( allOf(withId(R.id.appointmentCallCar), withParent(withId(R.id.callCarLayout)), isDisplayed())); //執行該控件的點擊操作 appCompatImageView.perform(click()); //根據id找到控件,並判斷是否可見 ViewInteraction appCompatEditText = onView( allOf(withId(R.id.phoneNumber), isDisplayed())); //執行替換文本操作,說白了就是輸入文本,輸入完畢之後關閉輸入法鍵盤 appCompatEditText.perform(replaceText("18894001263"), closeSoftKeyboard()); //根據id和顯示的文本內容找到控件,並判斷是否可見 ViewInteraction appCompatButton2 = onView( allOf(withId(R.id.getPassword), withText("獲取驗證碼"), isDisplayed())); //執行該控件的點擊操作 appCompatButton2.perform(click()); //根據id找到控件,並判斷是否可見 ViewInteraction appCompatEditText2 = onView( allOf(withId(R.id.password), isDisplayed())); //執行替換文本操作,說白了就是輸入文本,輸入完畢之後關閉輸入法鍵盤 appCompatEditText2.perform(replaceText("2454"), closeSoftKeyboard()); //根據id和顯示的文本內容找到控件,並判斷是否可見 ViewInteraction appCompatButton3 = onView( allOf(withId(R.id.login), withText("登錄"), isDisplayed())); //執行該控件的點擊操作 appCompatButton3.perform(click()); } }
這裡還需要說明一點,當所有的測試用例執行完畢之後,Espresso會自動關閉界面,根據動圖也可以看到,當點擊完登陸按鈕之後,又回到了系統屏幕界面.
當測試完畢之後,在Android Studio的”Run”控制台可以看到測試結果,如下圖:
如果測試順利通過,會在下圖中左側顯示All Test Passed:
如果測試沒有通過,則會在右側的控制台中輸入錯誤信息,我們可以根據這些錯誤信息修改我們的代碼然後再次進行測試.
以上的測試代碼只是測試了啟動和登陸功能,可以看到,套路都是一樣的:根據id或文本等條件找到控件,然後執行控件的相關操作,這些代碼都是重復的,唯一變化的就是執行的操作和查找的條件不一樣,我們試想一下,如果項目一旦很大,我們一個一個手動的編寫測試代碼是不是很麻煩,並且都是一樣的套路,沒有一點技術含量.如果有種方式能自動生成這些測試代碼,而我們只需要根據具體的測試情況修改甚至不改這些自動生成的代碼就能完成測試,是不是就可以極大的節省我們的時間?而恰好Android Studio2.2版本提供了一個使用Espresso框架進行測試的圖形化界面–Record Espresso Test功能,通過這個功能,我們只需要把軟件運行到真機或模擬器上,然後就可以像平常手動測試軟件一樣,按照業務邏輯點擊/滑動即可,Record Espresso Test功能會自動生成相應的測試代碼,我們運行生成的測試代碼,Espresso就可以自動的按照我們剛才操作的順序自動的完成測試,是不是很方便啊.關於如何使用Record Espresso Test功能,以後會單獨寫一篇文章來說明,其實操作起來也很簡單,大家可以先自己去嘗試一下.
之前對線程也寫過幾篇文章,不過倒是沒有針對android,因為java與android在線程方面大部分還是相同,不過本篇我們要介紹的是android的專屬類Handler
我們有一個TextView,其裡面的內容是可以通過代碼動態改變的,我們想用一張圖片作為TextView的背景,實現類似於手機QQ對話中的氣泡文本效果。TextView定義
大家可以發現, 原生的Image控件無法實現等比放大後無丟失顯示。如: 有一張20x10的圖片, 要放入一個40x30的顯示區域內.1. cover模式(默認),圖片放大
今天給大家帶來Android畫板功能的簡單實現,以下是效果圖: 以下是關鍵源碼: import android.content.Conte