Android自動化補記-淺談Android Espresso自動化測試框架

早在去年的時候,經過一階段針對“濃縮咖啡”Espresso框架的仔細品嚐,淺品深品,不得不說,畢竟是谷歌力推的測試框架,就連AndroidStudio默認創建工程後都自帶編譯的包,果然不出意外的這個框架十分的強大,強大到什麼程度,比如只在UI線程靜止的時候再進行操作,自動異步更新測試監聽以及UI線程,意味着只要定位準,手機不卡等一些因素不需要擔心超時等頁面刷新不同步等問題,不過也是有缺點的,就是學習成本比較高,特別是針對不同自定義控件以及情況的適配,網上有的文檔資源特別少;當然,整體來說依舊是白盒自動化測試的首選,最好結合一下UiAutoMation使用,效果不錯;

起初這個框架最讓我眼前一亮的是這個框架在編寫自動化代碼的方式,通過直接引用靜態方法來直接調用測試Api,同時同時同時(來個三遍),做到了定位+操作+檢驗,三個測試操作,一行代碼搞定的精妙語法,哇,很強,我怎麼就想不到封裝成這個樣兒;

後來逐步的使用,才發現這個水有點兒深,適配控件自動化着實有點兒費勁,這裏先不做這方面的討論了,只是淺淺的分享一下;

回到正題,下面簡單介紹一下如何使用這個框架來編寫測試代碼

首先,拿到我們測試的源碼,爲什麼要拿源碼來白盒測試而不是黑盒這點會面會詳細的說明,然後將項目導入到Android Studio中,首先保證導入的App能正常運行,再進行下一步的寫自動化代碼;

這裏我也沒有用什麼源碼,自己新建了一個Android項目,正常情況現在的AS一般會自動的將項目架構構建成如下所示的文件架構;

首先,第一個就是做Android開發使用的位置,裏面會有Apk源碼等等;

第二個是androidTest的文件夾所在的位置,依賴於Apk;

第三個就是Test的文件夾,主要是放一些單元測試的測試類;

我們Espresso測試類編寫在androidTest的文件夾中;

 

簡單說一下這個App的功能:

點擊“Click There One”下方的TextView顯示Click One Btn;

點擊“Click There Two”下方的TextView顯示Click Two Btn;

 

“Click There One”的id爲:btn_one_click,text爲:Click There one

“Click There Two”的id爲:btn_two_click,text爲:Click There two

注:頁面代碼以及Activity代碼在最後;

頁面如下圖所示;

 

自動化測試代碼如下

package heepay.com.espressoautotest;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.junit.Assert.*;

/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {

    @Rule
    public ActivityTestRule<MainActivity> rule = new ActivityTestRule<MainActivity>(MainActivity.class, true);

    @Test
    public void useAppContext() throws Exception {
        /*正常正確的情況*/
        //通過text的方式定位按鈕,然後click點擊
        onView(withText("Click There one")).perform(click());
        //判斷textView有沒有正常改變
        onView(withId(R.id.tv_test)).check(matches(withText("Clicked One Btn")));

        onView(withText("Click There two")).perform(click());
        onView(withId(R.id.tv_test)).check(matches(withText("Clicked Two Btn")));

        //通過id的方式定位按鈕,然後click點擊
        onView(withId(R.id.btn_one_click)).perform(click());
        //判斷textView有沒有正常改變
        onView(withId(R.id.tv_test)).check(matches(withText("Clicked One Btn")));

        onView(withId(R.id.btn_two_click)).perform(click());
        onView(withId(R.id.tv_test)).check(matches(withText("Clicked Two Btn")));

        /*判斷出異常的情況*/
        //根據Id找到btn,判斷text是否是Btn----後再點擊
        onView(withId(R.id.btn_one_click)).check(matches(withText("Btn----"))).perform(click());

        //通過id的方式定位按鈕,然後click點擊
        onView(withId(R.id.btn_two_click)).perform(click());
        //判斷textView的內容是否一致
        onView(withId(R.id.tv_test)).check(matches(withText("Error")));

    }
}

有註釋,應該都很好理解,而且也是很簡單的功能,暫不涉及很負責的東西;

前4條正常情況的測試,分別是根據Text獲取兩個按鈕的定位,然後點擊,之後判斷文本是否正常的改變;

後2條是異常結果的測試:

首先是根據id獲取到按鈕1,檢查按鈕的text是否爲Btn----,不是則不進行後面的操作;

其次是根據id獲取到按鈕1,點擊後,檢查文本是否變成“Error”

在執行結束測試後,結果如下所示

第一個異常的結果:

 

第二個異常的結果:

 

實在是太熟悉了,乍一看不知道的還以爲是Junit下面做的測試,好吧,Espresso實際上是在Junit上面做的擴充,基於Junit的框架,所以當出現跟預期的值不同的時候,會出現

Expected: with text: is "Error"這樣的字眼

一個簡單的例子結束,這是基於源代碼的白盒自動化測試;現在我們來說一下之前的問題爲什麼不推薦使用Espresso來進行黑盒測試;

第一:不知道大家注意到代碼裏面沒有,Espresso是可以直接從Apk的R文件中根據Id來查詢控件進行定位的,效率不知道高了多少,但是如果是黑盒測試,拿不到源碼,這個withId功能將無法使用;

第二:成本很高,我也查到過網上面有的人也成功過Espresso黑盒測試,顯示把測試代碼所在的Apk包名與待測試的Apk包名一致,然後簽名等等等等,成本實在是太高,而且還不一定成功,非常的不推薦;

第三:如果想正常的在自己的測試代碼Apk中編寫Espresso(就跟我之前一樣),然後來測試待測試的Apk的功能,這個暫時無法實現,基本上也沒有辦法實現;

整體原因基於這三點,我們來着重分析一下第三點;

首先簡單說一下UiAutomator跟Espresso的區別,UiAutomator不需要綁定Activity,直接操作頁面顯示元素來完成自動化;而Espresso是需要綁定待測試的Activity來作爲入口來進行測試;

第一:這就意味着,黑盒?你不知道內部Activity的入口,就無法進行綁定;

第二:綁定?這個也是無法實現的,即使通過java的反射機制我們可以根據packagePath來對第三方App翻天覆地的搞,PackageManager獲取內部的Activity名稱等等,然後想的肯定是難道都拿到這些東西了,難道不能使用Intent,或者Instrumentation來啓動App來進行測試嗎;

首先,Intent+context是可以啓動App,但是!!!無法爲下一步的onView做鋪墊,最後的結果一定是如下圖所示;

即使打開了待測試的App,依舊是找不到可以測試的Activity來進行測試;

我也不信邪,開始各種翻源碼,準備自己仿寫一個ActivityRule,發現Espresso的綁定Activity的方式是基於Instrumentration實現的,然後源碼中啓動綁定Activity的方式正是剛纔異常錯誤中的startActivitySync(),然後就開始鑽如何使用這個方法來啓動,如果是在源碼中測試,什麼都不需要,只需把待測試的Activity名.class傳進來即可,裏面的啓動流程簡單說是,先根據context獲取App的packagePath,然後拿着這個packagePath以及這個Activity名.class去綁定Intent啓動;

隨後就開始了仿寫,仿寫的很成功,待測試的Apk的路徑,context,內部所有的Activity名等等全都成功拿到,然後測試就來了個這樣的一個提示,如下所示;

不能運行該應用,原因原進程試圖分解到新的進程,最終,我去官方文檔中去翻了翻看,發現了一句話,很感人;

大意:它不允許您啓動在不同進程中運行的Activity。此外,如果給定的意圖解析爲多個活動,而不是顯示用戶選擇Activity的對話框,則會引發異常。

說到底就是,不允許跨進程的啓動Activity,基本上也就意味着不能跨App的綁定測試Activity;一首涼涼送給自己,多麼6的框架結果不能使用起來(雖然也有缺點),很藍瘦;

最後,測試程序的Activity源碼以及頁面源碼還是貼一下吧,如下,直接複製即可;

MainActivity 代碼:

package heepay.com.espressoautotest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView tv_test = findViewById(R.id.tv_test);
        Button btn_one_click = findViewById(R.id.btn_one_click);
        Button btn_two_click = findViewById(R.id.btn_two_click);
        btn_one_click.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tv_test.setText("Clicked One Btn");
            }
        });
        btn_two_click.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                tv_test.setText("Clicked Two Btn");
            }
        });
    }
}

頁面代碼:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="heepay.com.espressoautotest.MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <Button
            android:id="@+id/btn_one_click"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Click There one" />

        <Button
            android:id="@+id/btn_two_click"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/btn_one_click"
            android:text="Click There two" />


        <TextView
            android:id="@+id/tv_test"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Click There two"
            android:layout_centerInParent="true"
            />

    </RelativeLayout>

</android.support.constraint.ConstraintLayout>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章