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测试异步代码
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,则它将整个视图层次结构包含在错误消息中。这对于分析问题很有用。
使用Android SDK管理器安装Android支持存储库。
要使用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.......
}
建议在用于测试的Android设备上打开动画。动画可能会使Espressos检查资源浪费。
创建一个名为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;
}
}
}
按照Espresso的Gradle构建文件的配置中所述执行设置。
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")));
}
}
右键单击您的测试,然后选择“运行”。有关详细信息,请参见运行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;
要查找视图,请将该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 ")))); |
ViewInteraction并DataInteraction允许ViewAction通过perform方法的类型的对象指定要进行测试的操作。本ViewAction类提供了最常用的操作,如辅助方法:
ViewActions.click()
ViewActions.pressKey()
ViewActions.clearText()
该perform方法再次返回一个类型的对象ViewInteraction,您可以在该对象上执行更多操作或验证结果。它还使用varags作为参数,即,您可以同时将多个操作传递给它。
调用该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”字符串 |
通过,
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);
}
}
如果您在中指定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")));
}
}
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());
通过检测,您可以批准测试执行的权限。
@Before
public void grantPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getInstrumentation().getUiAutomation().executeShellCommand(
"pm grant " + getTargetContext().getPackageName()
+ " android.permission.CALL_PHONE");
}
}
Android Studio提供了运行 记录Espresso测试菜单项,使您可以记录与应用程序的交互并从中创建Espresso测试。
您还可以访问要测试的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.1.使用Android Studio
4.11.2.使用Gradle
使用connectedCheck Gradle中的任务直接通过Gradle运行测试。
有一个示例,您可以单击列表项并检查要显示的吐司。
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()));
}
}
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()));
}
}
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
使用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);
}
}
为该Activity编写一个Espresso测试,以测试以下各项:
检查MainActivity的布局是否包含具有 R.id.button ID 的按钮确保按钮上的文本为“开始新Activity”确保如果调用了getActivity.onClick()方法,则将触发正确的意图。该意图应包含额外的字符串(hasExtra(“ URL”,“ https://www.vogella.com/”)。
您的测试代码应类似于以下示例代码。
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/"));
}
}
在本练习中,您将启动第二个Activity并验证已经开始的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"))));
}
}
使用名为的包创建一个项目,该包
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")));
}
}