Android 浓缩咖啡。 perform(click()) 等待通过单击按钮启动的任务完成
Android Espresso. perform(click()) waits for the tasks started by a button click to complete
我正在编写一个仪器 Android 测试。当我单击按钮 sample_btn
时,Task
和 DummyTask
开始。当 Task
结束时,EditText sample_text
变得可见,我可以在那里输入一些文本。
当我注意到以下事实时,我感到很惊讶。 onView(withId(R.id.sample_btn)).perform(click());
等待 Task
和 DummyTask
完成。为什么?
MainActivity
package com.sample;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import java.lang.ref.WeakReference;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.sample_btn).setOnClickListener(this);
}
@Override
public void onClick(final View v) {
switch (v.getId()) {
case R.id.sample_btn:
Log.d("UnderstandIsDisplayed", "Before sample_btn click IN CODE");
new Task(this).execute();
new DummyTask().execute();
Log.d("UnderstandIsDisplayed", "After sample_btn click IN CODE");
break;
}
}
private static class DummyTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(final Void... params) {
try {
Log.d("UnderstandIsDisplayed", "DummyTask doInBackground");
Thread.sleep(8000);
Log.d("UnderstandIsDisplayed", "DummyTask doInBackground done");
} catch (InterruptedException e) {
}
return null;
}
}
private static class Task extends AsyncTask<Void, Void, Void> {
final WeakReference<MainActivity> mActivityWeakReference;
Task(MainActivity activity) {
mActivityWeakReference = new WeakReference<>(activity);
}
@Override
protected Void doInBackground(final Void... params) {
try {
Log.d("UnderstandIsDisplayed", "doInBackground");
Thread.sleep(4000);
Log.d("UnderstandIsDisplayed", "doInBackground done");
} catch (InterruptedException e) {
}
return null;
}
@Override
protected void onPostExecute(final Void aVoid) {
super.onPostExecute(aVoid);
MainActivity mainActivity = mActivityWeakReference.get();
if (mainActivity != null) {
mainActivity.findViewById(R.id.sample_text).setVisibility(View.VISIBLE);
Log.d("UnderstandIsDisplayed", "sample_text VISIBLE");
}
}
}
}
MainActivityTest
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
private MainActivity mMainActivity;
public MainActivityTest() {
super(MainActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
mMainActivity = getActivity();
}
public void testFoo() {
Log.d("UnderstandIsDisplayed", "Before sample_btn click");
onView(withId(R.id.sample_btn)).perform(click());
Log.d("UnderstandIsDisplayed", "After sample_btn click");
// TODO: Wait for sample_text to become visible
onView(withId(R.id.sample_text)).perform(typeText("sample_text"));
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#336633"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.sample.MainActivity">
<Button
android:id="@+id/sample_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sample_btn"/>
<EditText
android:id="@+id/sample_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"/>
</LinearLayout>
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.sample"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.0.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
}
这是我在 运行 应用程序本身时看到的:
09-07 12:09:02.623 22356-22356/com.sample D/UnderstandIsDisplayed: Before sample_btn click IN CODE
09-07 12:09:02.629 22356-22660/com.sample D/UnderstandIsDisplayed: doInBackground 1562
09-07 12:09:02.631 22356-22356/com.sample D/UnderstandIsDisplayed: After sample_btn click IN CODE
09-07 12:09:06.670 22356-22660/com.sample D/UnderstandIsDisplayed: doInBackground done
09-07 12:09:06.672 22356-22356/com.sample D/UnderstandIsDisplayed: sample_text VISIBLE
09-07 12:09:06.680 22356-22771/com.sample D/UnderstandIsDisplayed: DummyTask doInBackground 1563
09-07 12:09:14.721 22356-22771/com.sample D/UnderstandIsDisplayed: DummyTask doInBackground done
这是我在 运行 testFoo
:
时看到的
09-07 12:10:24.357 24892-24911/com.sample D/UnderstandIsDisplayed: testFoo = 1555
09-07 12:10:24.357 24892-24911/com.sample D/UnderstandIsDisplayed: Before sample_btn click
09-07 12:10:24.429 24892-24892/com.sample D/UnderstandIsDisplayed: Before sample_btn click IN CODE
09-07 12:10:24.431 24892-24954/com.sample D/UnderstandIsDisplayed: doInBackground 1563
09-07 12:10:24.431 24892-24892/com.sample D/UnderstandIsDisplayed: After sample_btn click IN CODE
09-07 12:10:28.465 24892-24954/com.sample D/UnderstandIsDisplayed: doInBackground done
09-07 12:10:28.469 24892-24892/com.sample D/UnderstandIsDisplayed: sample_text VISIBLE
09-07 12:10:28.472 24892-24936/com.sample D/UnderstandIsDisplayed: DummyTask doInBackground 1558
09-07 12:10:36.483 24892-24936/com.sample D/UnderstandIsDisplayed: DummyTask doInBackground done
09-07 12:10:36.702 24892-24911/com.sample D/UnderstandIsDisplayed: After sample_btn click
使用Espresso
,您只能在测试环境下在您的应用程序内部进行操作。这意味着 Espresso 执行任何操作都需要在应用程序的主线程上运行。它检查 UI 线程何时为 idle()
,如果不是,则等待直到 UI 线程再次空闲。
检查:http://dev.jimdo.com/2014/05/09/wait-for-it-a-deep-dive-into-espresso-s-idling-resources/
在某些情况下,它会产生 Espressso IdlingResources
错误,例如 AppNotIdleState
:https://developer.android.com/reference/android/support/test/espresso/AppNotIdleException.html
示例 1#:https://github.com/81813780/AVLoadingIndicatorView/issues/16
示例 2#:
要处理此问题,您可以创建自己的 IdlingResource
方法,当 UI 线程对他来说 idle
时,它会说 Espresso
。
检查:https://google.github.io/android-testing-support-library/docs/espresso/idling-resource/index.html
示例:http://blog.sqisland.com/2015/04/espresso-custom-idling-resource.html
如果它不起作用,请尝试与 Espresso
一起使用另一个名为 uiatomator
的 Google 测试框架,这可能会帮助您解决这个问题。
在我的例子中,我自己编写 IdlingResources
没有用,但是将 Espresso
与 Robotium
混合(不等待空闲 UI 线程)和uiautomator
完成了它的工作。
希望对您有所帮助
AsyncTask
处理已融入 UiControllerImpl.loopMainThreadUntilIdle()
中的 espresso 框架。在大多数情况下,espresso 开箱即用地处理一些后台工作非常方便。不幸的是,没有可用的解决方案来关闭任何提供的处理程序(UI 线程同步,异步任务)。这使得测试进度指示器或测试中断非常复杂。
espresso bug tracker 中有一个未解决的问题,但没有引起太多关注。
我正在编写一个仪器 Android 测试。当我单击按钮 sample_btn
时,Task
和 DummyTask
开始。当 Task
结束时,EditText sample_text
变得可见,我可以在那里输入一些文本。
当我注意到以下事实时,我感到很惊讶。 onView(withId(R.id.sample_btn)).perform(click());
等待 Task
和 DummyTask
完成。为什么?
MainActivity
package com.sample;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import java.lang.ref.WeakReference;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.sample_btn).setOnClickListener(this);
}
@Override
public void onClick(final View v) {
switch (v.getId()) {
case R.id.sample_btn:
Log.d("UnderstandIsDisplayed", "Before sample_btn click IN CODE");
new Task(this).execute();
new DummyTask().execute();
Log.d("UnderstandIsDisplayed", "After sample_btn click IN CODE");
break;
}
}
private static class DummyTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(final Void... params) {
try {
Log.d("UnderstandIsDisplayed", "DummyTask doInBackground");
Thread.sleep(8000);
Log.d("UnderstandIsDisplayed", "DummyTask doInBackground done");
} catch (InterruptedException e) {
}
return null;
}
}
private static class Task extends AsyncTask<Void, Void, Void> {
final WeakReference<MainActivity> mActivityWeakReference;
Task(MainActivity activity) {
mActivityWeakReference = new WeakReference<>(activity);
}
@Override
protected Void doInBackground(final Void... params) {
try {
Log.d("UnderstandIsDisplayed", "doInBackground");
Thread.sleep(4000);
Log.d("UnderstandIsDisplayed", "doInBackground done");
} catch (InterruptedException e) {
}
return null;
}
@Override
protected void onPostExecute(final Void aVoid) {
super.onPostExecute(aVoid);
MainActivity mainActivity = mActivityWeakReference.get();
if (mainActivity != null) {
mainActivity.findViewById(R.id.sample_text).setVisibility(View.VISIBLE);
Log.d("UnderstandIsDisplayed", "sample_text VISIBLE");
}
}
}
}
MainActivityTest
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
private MainActivity mMainActivity;
public MainActivityTest() {
super(MainActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
mMainActivity = getActivity();
}
public void testFoo() {
Log.d("UnderstandIsDisplayed", "Before sample_btn click");
onView(withId(R.id.sample_btn)).perform(click());
Log.d("UnderstandIsDisplayed", "After sample_btn click");
// TODO: Wait for sample_text to become visible
onView(withId(R.id.sample_text)).perform(typeText("sample_text"));
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#336633"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.sample.MainActivity">
<Button
android:id="@+id/sample_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sample_btn"/>
<EditText
android:id="@+id/sample_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"/>
</LinearLayout>
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.sample"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.0.1'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
}
这是我在 运行 应用程序本身时看到的:
09-07 12:09:02.623 22356-22356/com.sample D/UnderstandIsDisplayed: Before sample_btn click IN CODE
09-07 12:09:02.629 22356-22660/com.sample D/UnderstandIsDisplayed: doInBackground 1562
09-07 12:09:02.631 22356-22356/com.sample D/UnderstandIsDisplayed: After sample_btn click IN CODE
09-07 12:09:06.670 22356-22660/com.sample D/UnderstandIsDisplayed: doInBackground done
09-07 12:09:06.672 22356-22356/com.sample D/UnderstandIsDisplayed: sample_text VISIBLE
09-07 12:09:06.680 22356-22771/com.sample D/UnderstandIsDisplayed: DummyTask doInBackground 1563
09-07 12:09:14.721 22356-22771/com.sample D/UnderstandIsDisplayed: DummyTask doInBackground done
这是我在 运行 testFoo
:
09-07 12:10:24.357 24892-24911/com.sample D/UnderstandIsDisplayed: testFoo = 1555
09-07 12:10:24.357 24892-24911/com.sample D/UnderstandIsDisplayed: Before sample_btn click
09-07 12:10:24.429 24892-24892/com.sample D/UnderstandIsDisplayed: Before sample_btn click IN CODE
09-07 12:10:24.431 24892-24954/com.sample D/UnderstandIsDisplayed: doInBackground 1563
09-07 12:10:24.431 24892-24892/com.sample D/UnderstandIsDisplayed: After sample_btn click IN CODE
09-07 12:10:28.465 24892-24954/com.sample D/UnderstandIsDisplayed: doInBackground done
09-07 12:10:28.469 24892-24892/com.sample D/UnderstandIsDisplayed: sample_text VISIBLE
09-07 12:10:28.472 24892-24936/com.sample D/UnderstandIsDisplayed: DummyTask doInBackground 1558
09-07 12:10:36.483 24892-24936/com.sample D/UnderstandIsDisplayed: DummyTask doInBackground done
09-07 12:10:36.702 24892-24911/com.sample D/UnderstandIsDisplayed: After sample_btn click
使用Espresso
,您只能在测试环境下在您的应用程序内部进行操作。这意味着 Espresso 执行任何操作都需要在应用程序的主线程上运行。它检查 UI 线程何时为 idle()
,如果不是,则等待直到 UI 线程再次空闲。
检查:http://dev.jimdo.com/2014/05/09/wait-for-it-a-deep-dive-into-espresso-s-idling-resources/
在某些情况下,它会产生 Espressso IdlingResources
错误,例如 AppNotIdleState
:https://developer.android.com/reference/android/support/test/espresso/AppNotIdleException.html
示例 1#:https://github.com/81813780/AVLoadingIndicatorView/issues/16
示例 2#:
要处理此问题,您可以创建自己的 IdlingResource
方法,当 UI 线程对他来说 idle
时,它会说 Espresso
。
检查:https://google.github.io/android-testing-support-library/docs/espresso/idling-resource/index.html
示例:http://blog.sqisland.com/2015/04/espresso-custom-idling-resource.html
如果它不起作用,请尝试与 Espresso
一起使用另一个名为 uiatomator
的 Google 测试框架,这可能会帮助您解决这个问题。
在我的例子中,我自己编写 IdlingResources
没有用,但是将 Espresso
与 Robotium
混合(不等待空闲 UI 线程)和uiautomator
完成了它的工作。
希望对您有所帮助
AsyncTask
处理已融入 UiControllerImpl.loopMainThreadUntilIdle()
中的 espresso 框架。在大多数情况下,espresso 开箱即用地处理一些后台工作非常方便。不幸的是,没有可用的解决方案来关闭任何提供的处理程序(UI 线程同步,异步任务)。这使得测试进度指示器或测试中断非常复杂。
espresso bug tracker 中有一个未解决的问题,但没有引起太多关注。