Espresso的詳細(xì)使用

本篇詳細(xì)介紹了Espresso的使用方式.

Espresso 測試代碼位置和靜態(tài)導(dǎo)入

Espresso 測試代碼必須放在 app/src/androidTest 目錄下.

為了簡化 Espresso API 的使用, 強(qiáng)烈建議使用以下靜態(tài)導(dǎo)入. 可以允許在沒有類前綴的前提下訪問這些靜態(tài)方法.

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
import static android.support.test.espresso.action.ViewActions.typeText;
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;

ViewMatcher 的使用

查找一個 View, 需要使用 onView() 方法和視圖匹配器(view matcher)來選擇正確的 View. 如果你使用的是 AdapterView, 需要使用 onData() 方法而不是 onView()方法. onView() 返回的是一個 ViewInteraction 類型的對象, onData() 返回的是一個 DataInteraction 類型的對象.

表1 中介紹了可用的匹配器.

Espresso matchers

表 1 Espresso 匹配器

執(zhí)行操作(perform action)

ViewInteraction 和 DataInteraction 可以通過 perform 方法和 ViewActions 類型的對象指定某種操作. ViewActions 類為最常見的操作了提供了相應(yīng)的輔助方法. 像:

  • ViewActions.click()
  • ViewActions.typeText()
  • ViewActions.pressKey()
  • ViewActions.clearText()
  • ViewActions.replaceText()

perform() 方法還會再次返回一個 ViewInteraction 的對象, 也就是說你可以執(zhí)行更多的操作或者驗證結(jié)果. 而且你也可以通過 perform() 方法的可變參數(shù)來指定多個操作.

驗證測試結(jié)果

調(diào)用 ViewInteraction.check() 方法可以斷言一個 View 的狀態(tài). 這個方法需要一個 ViewAssertion 類型的對象作為參數(shù). ViewAssertion 類提供了用于創(chuàng)建這些對象的輔助方法.

  • maches() - Hamcrest 匹配器
  • doesNotExist() - 斷言選中的 View 不存在

你也可以使用強(qiáng)大的 Hamcrest 匹配器. 下面給出了部分示例:

onView(withText(startsWith("ABC"))).perform(click());// 1.

onView(withText(endsWith("YYZZ"))).perform(click()); // 2.

onView(withId(R.id.viewId)).
  check(matches(withContentDescription(containsString("YYZZ")))); // 3.

onView(withText(equalToIgnoringCase("xxYY"))).perform(click()); // 4.

onView(withText(equalToIgnoringWhiteSpace("XX YY ZZ"))).perform(click()); // 5.

onView(withId(R.id.viewId)).check(matches(withText(not(containsString("YYZZ"))))); // 6.

① 匹配以文本 "ABC" 開頭的 View

② 匹配以文本 "YYZZ" 結(jié)尾的 View

③ 匹配指定 id View 的且檢查文本內(nèi)容包含 "YYZZ"

④ 匹配一個含有指定字符串的 View, 并且該字符串忽略大小寫

⑤ 匹配一個含有指定字符串的 View, 并且該字符串忽略空白字符

⑥ 匹配指定 id View 的且檢查文本內(nèi)容不包含 "YYZZ"

訪問 instrumentation API

通過 InstrumentationRegistry.getTargetContext() 可以訪問到測試應(yīng)用的上下文, 例如你想使用字符串 id 而不是 R.id, 下面的方法可以幫助你實(shí)現(xiàn):

/**
 * Created by zhanglulu on 2018/3/6.
 */
@RunWith(AndroidJUnit4.class)
public class EspressoTest2ActivityTest {

    @Rule
    public ActivityTestRule<EspressoTest2Activity> mRule =
            new ActivityTestRule<EspressoTest2Activity>(
                    EspressoTest2Activity.class);

    @Test
    public void buttonShouldUpdateText(){
        onView(withId(R.id.resultView)).perform(click());
        onView(withId(getResourceId("Click"))).check(matches(withText("Done")));
    }

    /**
     * 根據(jù) id 字符串獲取真實(shí) id
     * @param idStr
     * @return
     */
    private static int getResourceId(String idStr) {
        Context targetContext = InstrumentationRegistry.getTargetContext();
        String packageName = targetContext.getPackageName();
        return targetContext.getResources().getIdentifier(idStr, "id", packageName);
    }
}

配置Activity的啟動 Intent

如果你給 ActivityTestRule 構(gòu)造方法的第三個參數(shù)指定為 false, 則可以配置啟動 Intent, 并手動啟動測試 Activity.

/**
 * Created by zhanglulu on 2018/3/6.
 */
@RunWith(AndroidJUnit4.class)
public class EspressoTest2ActivityTest {

    @Rule
    public ActivityTestRule<EspressoTest2Activity> mRule =
            new ActivityTestRule<EspressoTest2Activity>(
                    EspressoTest2Activity.class, true, false);

    @Test
    public void demonstrateIntentPrep() {
        Intent intent = new Intent();
        intent.putExtra("input", "Test");
        mRule.launchActivity(intent);
        onView(withId(R.id.resultView)).check(matches(withText("Test")));
    }
}

Adapter Views

AdapterView 是一種特殊的組件, 它可以動態(tài)的從 Adapter 中加載數(shù)據(jù). 只有一部分?jǐn)?shù)據(jù)在 View 層次結(jié)構(gòu)中有真實(shí)的 View. onView() 不會為這些數(shù)據(jù)找到相應(yīng)的 View. 而 onData() 可以與 Adapter 進(jìn)行交互, 找到對應(yīng)的 View, 例如 ListView. 下面給出部分示例:

@Test
public void testList() {
    // 點(diǎn)擊 ListView 中文本為 "測試 15" 的 item
    onData(allOf(is(instanceOf(String.class)), is("測試 30")))
            .perform(click());
    onData(allOf(is(instanceOf(String.class)), is("測試 1")))
            .perform(click());
    onData(allOf(is(instanceOf(String.class)), is("測試 49")))
            .perform(click());
}

Espresso 測試添加權(quán)限

可以通過 instrumentation API 給你的測試執(zhí)行授予權(quán)限的操作.如下面示例:

@Before
public void grantPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        getInstrumentation().getUiAutomation().executeShellCommand(
                "pm grant " + getInstrumentation().getTargetContext().getPackageName()
                        + " android.permission.CALL_PHONE");
    }
}

Espresso UI Recorder

Android Studio 在測試菜單欄上提供了一個 Run ? Record Espresso Test 的功能, 它可以記錄下你與應(yīng)用程序的交互并從中創(chuàng)建 Espresso 測試.

run recorder

點(diǎn)擊運(yùn)行,之后 Android Studio 將記錄下你與應(yīng)用的交互行為,并生成測試代碼.

Recorder Test

訪問當(dāng)前的 Activity

你還可以訪問正在進(jìn)行測試的 Activity 對象,并調(diào)用它的方法. 例如,你有下面的 TestConfigureActivity, 并存在一個方法 configureActivity().

public class TestConfigureActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_configure);
    }
    // 外部調(diào)用
    public void configureActivity(String Uri) {
        // do something with this
    }

    public void onClick(View view) {
        startActivity(new Intent(this, SecondActivity.class));
    }
}

如果你想在測試方法中得到這個 Activity 并且執(zhí)行里面的方法, 你可以通過 ActivityTestRule 來獲取, 示例如下:

@Test
public void useAppContext() throws Exception{
    TestConfigureActivity activity = mRule.getActivity();
    activity.configureActivity("https://testurl");
    //do more
}

并且你也可以重寫 ActivityTestRule 類來配置你的測試 Activity.例如:

@Rule
    public ActivityTestRule<TestConfigureActivity> mRule =
            new ActivityTestRule<TestConfigureActivity>(TestConfigureActivity.class){
                @Override
                protected void beforeActivityLaunched() {
                    super.beforeActivityLaunched();
                    //Activity 啟動前做些事情
                }
    
                @Override
                protected void afterActivityLaunched() {
                    super.afterActivityLaunched();
                    //Activity 啟動后做些事情
                }
            };

你甚至可以拿到前臺的 Activity 的實(shí)例.這里著重說明一下, 你所測試的 Activity 不一定都在前臺顯示, 例如,你從當(dāng)前測試的 Activity (也就是你在 ActivityTestRule 傳入的 Activity) 跳轉(zhuǎn)到另外的一個 Activity, 此時在前臺的 Activity 就是另外的一個 Activity. 這個時候就可以通過 ActivityLifecycleMonitorRegistry 來獲取前臺的 Activity, 示例如下:

@Test
public void navigate() {
    onView(withText("Next")).perform(click());
    Activity activity = getActivityInstance();
    boolean b = (activity instanceof SecondActivity);
    assertTrue(b);
    // do more
}

public Activity getActivityInstance() {
    final Activity[] activity = new Activity[1];
    InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
        Activity currentActivity = null;
        Collection resumedActivities =
                ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(RESUMED);
        if (resumedActivities.iterator().hasNext()){
            currentActivity = (Activity) resumedActivities.iterator ().next();
            activity[0] = currentActivity;
        }
    });
    return activity[0];
}

需要注意的是 ActivityLifecycleMonitorRegistry 并不是標(biāo)準(zhǔn)的 API, 所以可能在后期名稱會發(fā)生變化.

運(yùn)行 Espresso 測試輸出測試報告

  1. 使用 Android Studio

右擊你的測試類, 并點(diǎn)擊運(yùn)行就 OK 了.

execute test for as
  1. 使用 Gradle

通過 Gradle 執(zhí)行 connectedCheck Task 可以執(zhí)行測試目錄下所有的測試用例.

excute test for gradle

執(zhí)行結(jié)束后將會在 app/build/reports 目錄下生成測試報告, 點(diǎn)擊打開 index.html 即可查看.

Test Reports

檢查 Taost

下面是一個點(diǎn)擊 Item ,并檢查 Toast 是否彈出的測試.

@Test
public void testToast() {
    onData(allOf(instanceOf(String.class), is("測試 20"))).perform(click());
    onView(withText(startsWith("點(diǎn)擊")))
            .inRoot(withDecorView(not(is(mRule.getActivity().getWindow().getDecorView()))))
            .check(matches(isDisplayed()));
}

通過Espresso Intents模擬Intent

Espresso 也提供了模擬 Intent 的功能. 它可以檢查一個 Activity 是否正常發(fā)出一個正確的 Intent.如果它接收了正確 Intent 則測試通過.

Espresso Intents 通過 com.android.support.test.espresso:espresso-intents 依賴提供.

使用 Espresso Intents 必須使用 IntentTestRule 而不是 ActivityTestRule.下面是一個簡單的示例:

/**
 * Created by zhanglulu on 2018/3/7.
 */
@RunWith(AndroidJUnit4.class)
public class TestIntent {

    @Rule
    public IntentsTestRule<EspressoTest1Activity> mIntentTestRule =
            new IntentsTestRule<EspressoTest1Activity>(EspressoTest1Activity.class);

    @Test
    public void triggerIntentTest() {
        onView(withId(R.id.switchActivity)).perform(click());
        intended(allOf(
                hasAction(Intent.ACTION_CALL),
                hasData("123456789"),
                toPackage(InstrumentationRegistry.getTargetContext().getPackageName()),
                hasExtra("input", "Test")
        ));
    }
}

更多詳細(xì)內(nèi)容請移步: https://developer.android.com/training/testing/espresso/intents.html#matching

敲行代碼再睡覺
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吸重,一起剝皮案震驚了整個濱河市鲫趁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌春寿,老刑警劉巖光坝,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尸诽,死亡現(xiàn)場離奇詭異,居然都是意外死亡教馆,警方通過查閱死者的電腦和手機(jī)逊谋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來土铺,“玉大人胶滋,你說我怎么就攤上這事”螅” “怎么了究恤?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長后德。 經(jīng)常有香客問我部宿,道長,這世上最難降的妖魔是什么瓢湃? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任理张,我火速辦了婚禮,結(jié)果婚禮上绵患,老公的妹妹穿的比我還像新娘雾叭。我一直安慰自己,他們只是感情好落蝙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布织狐。 她就那樣靜靜地躺著,像睡著了一般筏勒。 火紅的嫁衣襯著肌膚如雪移迫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天管行,我揣著相機(jī)與錄音厨埋,去河邊找鬼。 笑死捐顷,一個胖子當(dāng)著我的面吹牛揽咕,可吹牛的內(nèi)容都是我干的悲酷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼亲善,長吁一口氣:“原來是場噩夢啊……” “哼设易!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蛹头,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤顿肺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后渣蜗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屠尊,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年耕拷,在試婚紗的時候發(fā)現(xiàn)自己被綠了讼昆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡骚烧,死狀恐怖浸赫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赃绊,我是刑警寧澤既峡,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站碧查,受9級特大地震影響运敢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜忠售,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一传惠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稻扬,春花似錦涉枫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽困后。三九已至乐纸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間摇予,已是汗流浹背汽绢。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侧戴,地道東北人宁昭。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓跌宛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親积仗。 傳聞我的和親對象是個殘疾皇子疆拘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容