Android UI 测试框架Espresso详解

Android UI 测试框架Espresso详解

1. Espresso测试框架

2.提供Intents Espresso

2.1.安装

2.2.为Espresso配置Gradle构建文件

2.3.设备设置

3.练习:第一次Espresso测试

3.1.创建被测项目

3.2.调整应用程式build.gradle

3.3.创建您的Espresso测试

3.4.运行测试

4.有关编写Espresso单元测试的更多信息

4.1.Espresso测试的位置和所需的静态导入

4.2.使用ViewMatcher

4.3.执行动作

4.4.验证测试结果

4.5.访问检测API

4.6.配置Activity的开始意图

4.7.适配器视图

4.8.具有权限的Espresso测试

4.9.Espresso UI记录器

4.10.配置Activity

4.11.运行Espresso测试

4.12.检查Toast

5.用Intents EspressoIntents 模拟

6.练习:创建自定Espresso匹配器

7.练习:为Intents Espresso编写测试

7.1.创建经过测试的项目

7.2.编写测试

7.3.验证

8.练习:Activity的功能测试

8.1.编写Activity的功能测试

9.练习:使用Espresso测试异步代码


1. Espresso测试框架

Espresso是Android的测试框架,可简化编写可靠的用户界面测试的过程。

Google于2013年10月发布了Espresso框架。自其2.0版本以来,Espresso已成为Android支持存储库的一部分。

Espresso会自动将您的测试操作与应用程序的用户界面同步。该框架还确保您的Activity在测试运行之前就已经开始。它还让测试等到所有观察到的后台Activity都完成为止。

它旨在测试单个应用程序,但也可以用于跨应用程序进行测试。如果用于在应用程序外部进行测试,则只能执行黑盒测试,因为您无法访问应用程序外部的类。

Espresso基本上具有三个组成部分:

  • ViewMatchers-允许在当前视图层次结构中查找视图

  • ViewActions-允许对视图执行操作

  • ViewAssertions-允许声明视图状态

Espresso测试的案例构造如下:

Espresso测试

onView(ViewMatcher)       
 .perform(ViewAction)     
   .check(ViewAssertion); 

onView

-查找视图

perform

-对视图执行操作

check

-验证断言

 

以下代码演示了Espresso测试框架的用法。

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.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;

// image more code here...

// test statement
onView(withId(R.id.my_view))            // withId(R.id.my_view) is a ViewMatcher
        .perform(click())               // click() is a ViewAction
        .check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion

// new test
onView(withId(R.id.greet_button))
.perform(click())
.check(matches(not(isEnabled()));

如果Espresso无法通过找到一个视图ViewMatcher,则它将整个视图层次结构包含在错误消息中。这对于分析问题很有用。

2.提供Intents Espresso

2.1.安装

使用Android SDK管理器安装Android支持存储库。

Android UI 测试框架Espresso详解_第1张图片

2.2.为Espresso配置Gradle构建文件

要使用Espresso进行测试,请将以下依赖项添加到应用程序的Gradle构建文件中。

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    testImplementation 'junit:junit:4.12'

    // Android runner and rules support
    androidtestImplementation 'com.android.support.test:runner:0.5'
    androidtestImplementation 'com.android.support.test:rules:0.5'

    // Espresso support
    androidtestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })

    // add this for intent mocking support
    androidtestImplementation 'com.android.support.test.espresso:espresso-intents:2.2.2'

    // add this for webview testing support
    androidtestImplementation 'com.android.support.test.espresso:espresso-web:2.2.2'

}

确保在您的应用的构建文件中将android.support.test.runner.AndroidJUnitRunner指定为testInstrumentationRunner参数值。通过,packagingOptions您可能必须排除LICENSE.txt,具体取决于您所使用的库。以下清单是一个示例。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion '22.0.1'
    defaultConfig {
        applicationId "com.example.android.testing.espresso.BasicSample"
        minSdkVersion 10
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    packagingOptions {
        exclude 'LICENSE.txt'
    }
    lintOptions {
        abortOnError false
    }
}

dependencies {
    // as before.......
}

 

2.3.设备设置

建议在用于测试的Android设备上打开动画。动画可能会使Espressos检查资源浪费。

Android UI 测试框架Espresso详解_第2张图片

3.练习:第一次Espresso测试

3.1.创建被测项目

创建一个名为Espresso First的新Android项目,包名称为com.vogella.android.espressofirst。使用空白模板作为该项目的基础。

将生成的activity_main.xml布局文件更改为以下内容。



    

    

创建一个名为activity_second.xml的新文件。




    

使用以下代码创建一个Activity。

package com.vogella.android.espressofirst;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class SecondActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        TextView viewById = (TextView) findViewById(R.id.resultView);
        Bundle inputData = getIntent().getExtras();
        String input = inputData.getString("input");
        viewById.setText(input);
    }
}

同时调整MainActivity。

package com.vogella.android.espressofirst;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

public class MainActivity extends Activity {

    EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = (EditText) findViewById(R.id.inputField);
    }


    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.changeText:
                editText.setText("Lalala");
                break;
            case R.id.switchActivity:
                Intent intent = new Intent(this, SecondActivity.class);
                intent.putExtra("input", editText.getText().toString());
                startActivity(intent);
                break;
        }

    }
}

3.2.调整应用程式build.gradle

按照Espresso的Gradle构建文件的配置中所述执行设置。

3.3.创建您的Espresso测试

package com.vogella.android.espressofirst;

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.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;


@RunWith(AndroidJUnit4.class)
public class MainActivityEspressoTest {


    @Rule
    public ActivityTestRule mActivityRule =
        new ActivityTestRule<>(MainActivity.class);

    @Test
    public void ensureTextChangesWork() {
        // Type text and then press the button.
        onView(withId(R.id.inputField))
                .perform(typeText("HELLO"), closeSoftKeyboard());
        onView(withId(R.id.changeText)).perform(click());

        // Check that the text was changed.
        onView(withId(R.id.inputField)).check(matches(withText("Lalala")));
    }

    @Test
    public void changeText_newActivity() {
        // Type text and then press the button.
        onView(withId(R.id.inputField)).perform(typeText("NewText"),
                closeSoftKeyboard());
        onView(withId(R.id.switchActivity)).perform(click());

        // This view is in a different Activity, no need to tell Espresso.
        onView(withId(R.id.resultView)).check(matches(withText("NewText")));
    }
}

3.4.运行测试

右键单击您的测试,然后选择“运行”。有关详细信息,请参见运行Espresso测试。

4.有关编写Espresso单元测试的更多信息

4.1.Espresso测试的位置和所需的静态导入

Espresso测试必须放在app / src / androidTest文件夹中。

为了简化Espresso API的使用,建议添加以下静态导入。这允许在没有类前缀的情况下访问这些方法。

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;

4.2.使用ViewMatcher

要查找视图,请将该onView()方法与视图匹配器一起使用,以选择正确的视图。如果使用的是AdapterView,请使用onData()方法而不是onView()方法。在该onView()方法返回类型的对象DataInteraction。

下表描述了可用的匹配器。

withText(“ SOMETEXT”)

搜索带有指定文本的视图,例如onView(withText(“ User”))搜索带有文本“ User”的视图。

withId()

与withText相似,但搜索ID

Hamcrest Matchers

您可以使用Hamcrest匹配器,例如containsString或instanceOf()。也可以将Matchers与结合使用allOf()。如果要排除视图,请allOf()与一起使用not())。例:onView(allOf(withId(R.id.button_login), not(withText("Logout "))));

4.3.执行动作

ViewInteraction并DataInteraction允许ViewAction通过perform方法的类型的对象指定要进行测试的操作。本ViewAction类提供了最常用的操作,如辅助方法:

  • ViewActions.click()

  • ViewActions.pressKey()

  • ViewActions.clearText()

该perform方法再次返回一个类型的对象ViewInteraction,您可以在该对象上执行更多操作或验证结果。它还使用varags作为参数,即,您可以同时将多个操作传递给它。

4.4.验证测试结果

调用该ViewInteraction.check()方法以声明视图状态。此方法期望一个ViewAssertion对象作为输入。本ViewAssertions类提供了用于创建这些对象的辅助方法:

  • matches-Hamcrest matches

  • didNotExist-断言选择视图不存在

您可以使用功能强大的Hamcrest匹配器。下面给出一些示例:

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

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

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

onView(withText(equalToIgnoringCase("xxYY"))).perform(click()); 
 -
onView(withText(equalToIgnoringWhiteSpace("XX YY ZZ"))).perform(click()); 

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

 

1

匹配以“ ABC”模式开头的文本的视图

2

匹配文本以“ YYZZ”模式结尾的视图

3

匹配具有指定R.id的视图文本的内容描述,该描述在任何地方都包含“ YYZZ”字符串

4

匹配文本等于指定字符串的视图,忽略大小写:

5

匹配(大部分)忽略空格差异时,文本等于指定文本的视图

6

匹配具有指定R.id的特定视图的文本不包含“ YYZZ”字符串

 

4.5.访问检测API

通过,

InstrumentationRegistry.getTargetContext()

您可以访问应用程序的目标上下文。例如,如果要使用ID而不使用R.id,则可以使用以下帮助方法来确定它。

package testing.android.vogella.com.asynctask;

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;

@RunWith(AndroidJUnit4.class)
public class EspressoTest {

    @Rule
    public ActivityTestRule mActivityRule =
            new ActivityTestRule<>(MainActivity.class);

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

    private static int getResourceId(String s) {
        Context targetContext = InstrumentationRegistry.getTargetContext();
        String packageName = targetContext.getPackageName();
        return targetContext.getResources().getIdentifier(s, "id", packageName);
    }
}

4.6.配置Activity的开始意图

如果您在中指定false了第三个参数,则ActivityTestRule可以配置启动Activity的意图。如下面的代码示例所示。

package com.vogella.android.testing.espressosamples;

import android.content.Intent;
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.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

@RunWith(AndroidJUnit4.class)
public class SecondActivityTest {

    @Rule
    // third parameter is set to false which means the activity is not started automatically
    public ActivityTestRule rule =
        new ActivityTestRule(SecondActivity.class, true, false);

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

4.7.适配器视图

AdapterView是一种特殊类型的小部件,可从适配器动态加载其数据。在当前视图层次结构中,只有一部分数据具有真实视图。一个onView()搜索将找不到意见他们。 onData()可用于与适配器视图进行交互,例如ListView。以下给出了一些示例。

// click on an item of type String in a spinner
// afterwards verify that the view with the R.id.spinnertext_simple id contains "Eclipse"
onData(allOf(is(instanceOf(String.class)), is("Eclipse"))).perform(click());
onView(withId()).check(matches(withText(containsString("Eclipse")))); // normal view not adapter view

onData(allOf(is(instanceOf(Map.class)), hasEntry(equalTo("STR"), is("item: 50"))).perform(click());

onData(withItemContent("item: 60")).onChildView(withId(R.id.item_size)).perform(click());

4.8.具有权限的Espresso测试

通过检测,您可以批准测试执行的权限。

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

4.9.Espresso UI记录器

Android Studio提供了运行  记录Espresso测试菜单项,使您可以记录与应用程序的交互并从中创建Espresso测试。 Android UI 测试框架Espresso详解_第3张图片

Android UI 测试框架Espresso详解_第4张图片

4.10.配置Activity

您还可以访问要测试的Activity对象并在其上调用方法。例如,假设您要在Activity中调用方法。

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void configureMainActivity(String Uri) {
        // do something with this
    }
}

这configureMainActivity可以在您的测试中调用。

package com.vogella.android.myapplication;

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;

@RunWith(AndroidJUnit4.class)

public class ExampleInstrumentedTest {

    @Rule
    public ActivityTestRule mActivityRule =
            new ActivityTestRule(MainActivity.class);

    @Test
    public void useAppContext() throws Exception {
        MainActivity activity = mActivityRule.getActivity();
        activity.configureMainActivity("https://www.vogella.com/");
        // do more
    }
}

e.g.

您还可以覆盖中的方法ActivityTestRule,例如beforeActivityLaunched和afterActivityLaunched方法。

您还可以访问当前Activity。

@Test
public void navigate() {

        Activity instance = getActivityInstance();
        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(new Runnable( ) {
            public void run() {
                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不是API,因此可能会更改。允许访问当前Activity的Activity。

4.11.运行Espresso测试

4.11.1.使用Android Studio

右键单击您的测试,然后选择“运行”。 Android UI 测试框架Espresso详解_第5张图片

4.11.2.使用Gradle

使用connectedCheck Gradle中的任务直接通过Gradle运行测试。 Android UI 测试框架Espresso详解_第6张图片

4.12.检查Toast

有一个示例,您可以单击列表项并检查要显示的吐司。

package com.vogella.android.test.juntexamples;

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.onData;
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.RootMatchers.withDecorView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasToString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;

@RunWith(AndroidJUnit4.class)
public class MainActivityTestList {

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

    @Test
    public void ensureListViewIsPresent() throws Exception {
        onData(hasToString(containsString("Frodo"))).perform(click());
        onView(withText(startsWith("Clicked:"))).
        inRoot(withDecorView(
            not(is(rule.getActivity().
            getWindow().getDecorView())))).
            check(matches(isDisplayed()));
    }
}

5.用Intents EspressoIntents 模拟

Espresso还提供了模拟意图的选项。这使您可以检查Activity是否发出了正确的意图,如果Activity收到了正确的意图结果,则做出正确的反应。该

com.android.support.test.espresso:espresso-intents

库提供了Intents Espresso Intents 。有关设置,请参阅Espresso的Gradle构建文件的配置。

如果要在Espresso测试中使用Espresso Intent,请使用IntentsTestRule代替ActivityTestRule。

package testing.android.vogella.com.simpleactivity;


import android.support.test.espresso.intent.rule.IntentsTestRule;
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.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.toPackage;
import static android.support.test.espresso.matcher.ViewMatchers.withId;


@RunWith(AndroidJUnit4.class)
public class TestIntent {

    @Rule
    public IntentsTestRule mActivityRule =
        new IntentsTestRule<>(MainActivity.class);

    @Test
    public void triggerIntentTest() {
        onView(withId(R.id.button)).perform(click());
        intended(allOf(
                hasAction(Intent.ACTION_CALL),
                hasData(INTENT_DATA_PHONE_NUMBER),
                toPackage(PACKAGE_ANDROID_DIALER)));
    }

}

有关更多信息,请参见https://google.github.io/android-testing-support-library/docs/espresso/intents/。==使用Espresso测试异步代码

在没有框架支持的情况下测试异步具有挑战性。Espresso之前的典型做法是等待预定义的时间。或者CountDownLatch在测试代​​码中使用类的实例,并从异步处理中发出信号,表明处理已完成。Espresso使此操作变得更加容易,因为它可以自动监视后面的线程池AsynchronousTask。它还监视用户界面的事件队列。一旦没有任务在运行,vand只会继续进行测试。

如果您使用其他资源(例如IntentService),则需要实现IdlingResource。此实现必须监视此资源,并在Espresso框架中注册此监视器。

package com.vogella.android.espressointentservice;

import android.app.ActivityManager;
import android.content.Context;
import android.support.test.espresso.IdlingResource;

import java.util.List;

public class IntentServiceIdlingResource implements IdlingResource {

    ResourceCallback resourceCallback;
    private Context context;

    public IntentServiceIdlingResource(Context context) {
        this.context = context;
    }

    @Override
    public String getName() {
        return IntentServiceIdlingResource.class.getName();
    }

    @Override
    public void registerIdleTransitionCallback(
            ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }

    @Override
    public boolean isIdleNow() {
        boolean idle = !isIntentServiceRunning();
        if (idle && resourceCallback != null) {
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

    private boolean isIntentServiceRunning() {
        ActivityManager manager =
                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        // Get all running services
        List runningServices =
                manager.getRunningServices(Integer.MAX_VALUE);
        // check if our is running
        for (ActivityManager.RunningServiceInfo info : runningServices) {
            if (MyIntentService.class.getName().equals(
                    info.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
}

 

package com.vogella.android.espressointentservice;

import android.app.Instrumentation;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.Espresso;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.After;
import org.junit.Before;
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.hamcrest.Matchers.notNullValue;

@RunWith(AndroidJUnit4.class)
public class IntegrationTest {

    @Rule
    public ActivityTestRule rule = new ActivityTestRule(MainActivity.class);
    IntentServiceIdlingResource idlingResource;

    @Before
    public void before() {
        Instrumentation instrumentation
                = InstrumentationRegistry.getInstrumentation();
        Context ctx = instrumentation.getTargetContext();
        idlingResource = new IntentServiceIdlingResource(ctx);
        Espresso.registerIdlingResources(idlingResource);

    }
    @After
    public void after() {
        Espresso.unregisterIdlingResources(idlingResource);

    }

    @Test
    public void runSequence() {
        // this triggers our intent service, as we registered
        // Espresso for it, Espresso wait for it to finish
        onView(withId(R.id.action_settings)).perform(click());
        onView(withText("Broadcast")).check(matches(notNullValue()));
    }
}

6.练习:创建自定Espresso匹配器

Android提供了BoundedMatcher允许为特定视图类型创建Espresso视图匹配器的类。

让我们写一个匹配器来检查EditText字段的文本提示。参见http://qathread.blogspot.de/2014/03/descovering-espresso-for-android.html

public static Matcher withItemHint(String itemHintText) {
  checkArgument(!(itemHintText.equals(null)));
  return withItemHint(is(itemHintText));
}

public static Matcher withItemHint(final Matcher matcherText) {
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkNotNull(matcherText);
  return new BoundedMatcher(EditText.class) {

    @Override
    public void describeTo(Description description) {
      description.appendText("with item hint: " + matcherText);
    }

    @Override
    protected boolean matchesSafely(EditText editTextField) {
      return matcherText.matches(editTextField.getHint().toString());
    }
  };
}

可以通过以下代码使用它:

import static com.your.package.test.Matchers.withItemHint;
...
onView(withItemHint("test")).check(matches(isDisplayed()));

参见https://google.github.io/android-testing-support-library/docs/espresso/lists/#matching-data-using-ondata-and-a-custom-viewmatcher

7.练习:为Intents Espresso编写测试

7.1.创建经过测试的项目

使用testing.android.vogella.com.simpleactivity包和Empty Activity模板创建一个新的Android项目。SecondActivity通过文件将第二个Activity添加到项目中  新  Activity  空Activity。此Activity应使用至少一个布局TextView的ID TextView应为“ resultText”,其文本应设置为“ Started”。EditText在MainActivity类的布局中添加一个字段。

在MainActivity使用的布局中添加一个按钮。如果单击此按钮,则应该开始第二个Activity。EditText使用“文本”作为关键字,将文本字段多余地放入意图中。另外,使用键“ URL”将“ https://www.vogella.com/”字符串作为额外内容输入意图中。这是的一些示例代码MainActivity。

package testing.android.vogella.com.simpleactivity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onClick(View view) {
        Intent intent = new Intent(this, SecondActivity.class);
        intent.putExtra("URL", "https://www.vogella.com/");
        startActivity(intent);
    }
}

7.2.编写测试

为该Activity编写一个Espresso测试,以测试以下各项:

检查MainActivity的布局是否包含具有 R.id.button ID 的按钮确保按钮上的文本为“开始新Activity”确保如果调用了getActivity.onClick()方法,则将触发正确的意图。该意图应包含额外的字符串(hasExtra(“ URL”,“ https://www.vogella.com/”)。

7.3.验证

您的测试代码应类似于以下示例代码。

package testing.android.vogella.com.simpleactivity;


import android.support.test.espresso.intent.rule.IntentsTestRule;
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.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasExtra;
import static android.support.test.espresso.intent.matcher.IntentMatchers.toPackage;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.core.IsNull.notNullValue;


@RunWith(AndroidJUnit4.class)
public class TestIntent {

    @Rule
    public IntentsTestRule mActivityRule = new IntentsTestRule<>(MainActivity.class);

    @Test
    public void triggerIntentTest() {
        // check that the button is there
        onView(withId(R.id.button)).check(matches(notNullValue() ));
        onView(withId(R.id.button)).check(matches(withText("Start new activity")));
        onView(withId(R.id.button)).perform(click());
        intended(toPackage("testing.android.vogella.com.simpleactivity"));
        intended(hasExtra("URL", "https://www.vogella.com/"));
    }

}

8.练习:Activity的功能测试

在本练习中,您将启动第二个Activity并验证已经开始的Activity。

8.1.编写Activity的功能测试

package testing.android.vogella.com.simpleactivity;


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;


@RunWith(AndroidJUnit4.class)
public class TestSecondActivityIsStarted {

    @Rule
    public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void validateSecondActivity() {
        // check that the button is there
        onView(withId(R.id.button)).perform(click());
        onView(withId(R.id.resultText))
                .check(matches(withText(("Started"))));
    }

}

要测试视图的直接修改,请为该类创建以下测试SecondActivity类。

package testing.android.vogella.com.simpleactivity;


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.Espresso.pressBack;
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;


@RunWith(AndroidJUnit4.class)
public class SecondActivityFunctionalTest {

    @Rule
    public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void validateSecondActivity() {
        // check that the button is there
        onView(withId(R.id.button)).perform(click());
        onView(withId(R.id.resultText))
                .check(matches(withText(("Started"))));
        pressBack();
        onView(withId(R.id.button))
                .check(matches(withText(("Start new activity"))));

    }

}

 

9.练习:使用Espresso测试异步代码

使用名为的包创建一个项目,该包

testing.android.vogella.com.asynctask

允许通过按钮触发AsyncTask。

Activity的示例代码:

package testing.android.vogella.com.asynctask;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onClick(View view) {
        TextView textView = (TextView) findViewById(R.id.text);
        textView.setText("Running");
        myTask.execute("test");
    }

    final AsyncTask myTask = new AsyncTask() {

        @Override
        protected String doInBackground(String... arg0) {
            return "Long running stuff";
        }

        @Override
        protected void onPostExecute(String result) {
            TextView textView = (TextView) findViewById(R.id.text);
            textView.setText("Done");
        }

    };

}

测试的示例代码:

package testing.android.vogella.com.asynctask;

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.rules.TestName;
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;

@RunWith(AndroidJUnit4.class)
public class EspressoTest {

    @Rule
    public ActivityTestRule mActivityRule =
            new ActivityTestRule<>(MainActivity.class);

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

}

 

 

 

 

你可能感兴趣的:(Android,单元测试(Unit,test))