我的 android espresso 单元测试由于某些后台线程不空闲而被阻止。我如何找出哪个线程正在阻止我的应用程序执行?
android.support.test.espresso.AppNotIdleException: Looped for 246 iterations over 60 SECONDS. The following Idle Conditions failed ASYNC_TASKS_HAVE_IDLED.
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:580)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:92)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:56)
at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:184)
at android.support.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:115)
at android.support.test.espresso.ViewInteraction.perform(ViewInteraction.java:87)
at android.support.test.espresso.Espresso.closeSoftKeyboard(Espresso.java:159)
作为开始:
Espresso for Android 是完美且快速的测试自动化框架, 但它有一个重要的限制——你只能操作 在测试上下文下的应用程序内。
来自: http://qathread.blogspot.com/2015/05/espresso-uiautomator-perfect-tandem.html
Espresso
执行任何操作都需要在应用程序的主线程上操作。它检查 UI 线程何时处于 idle()
,如果不是,则等待直到 UI 线程再次空闲。
如果 UI Thread 空闲时间不是太长,就会产生
Espressso IdlingResources
错误,如 AppNotIdleException
,这意味着:
异常,表明App在指定的时间后仍未空闲。
IdlingResource
方法来表示 Espresso
,而 UI 线程则为 idle()
。
让我们更深入地了解一下问题:
Espresso 引入了
的概念,这是一个简单的 界面:IdlingResource
代表被测应用程序的资源 这可能会导致测试期间发生异步后台工作 执行
接口定义了三个方法:
- getName():必须返回一个非空字符串,用于标识空闲资源。
- isIdleNow():返回空闲资源当前的空闲状态。如果返回true,则
上的onTransitionToIdle()方法 注册的 ResourceCallback 必须之前已调用过。- registerIdleTransitionCallback(IdlingResource.ResourceCallbackcallback):通常该方法用于存储对
的引用 回调以通知其空闲状态发生变化。例如,等待空闲资源的实现 在 WebView 中完全加载的页面将如下所示:
public class WebViewIdlingResource extends WebChromeClient implements IdlingResource { private static final int FINISHED = 100; private WebView webView; private ResourceCallback callback; private WebViewIdlingResource(WebView webView) { this.webView = checkNotNull(webView, String.format("Trying to instantiate a \'%s\' with a null WebView", getName()))); // Shall we save the original client? Atm it's not used though. this.webView.setWebChromeClient(this); } @Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress == FINISHED && view.getTitle() != null && callback != null) { callback.onTransitionToIdle(); } } @Override public void onReceivedTitle(WebView view, String title) { if (webView.getProgress() == FINISHED && callback != null) { callback.onTransitionToIdle(); } } @Override public String getName() { return "WebView idling resource"; } @Override public boolean isIdleNow() { // The webView hasn't been injected yet, so we're idling if (webView == null) return true; return webView.getProgress() == FINISHED && webView.getTitle() != null; } @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { this.callback = resourceCallback; } }
创建自己的自定义闲置资源后,需要将其 通过致电注册 Espresso
。Espresso.registerIdlingResource(webViewIdlingResource)
来自:http://dev.jimdo.com/2014/05/09/wait-for-it-a-deep-dive-into-espresso-s-idling-resources/
如果它不起作用,请尝试与
Espresso
一起使用另一个名为[uiatomator
的Google测试框架,这可能会帮助你解决这个问题。
希望对你有帮助
我也有同样的问题。我编写了一个 DumThreadRule 来转储线程,以防测试失败。如果有人需要的话,这是代码:
class DumpThreadRule() : TestWatcher() {
private fun dumpThreads() {
val threads = Thread.getAllStackTraces()
threads.forEach {
System.err.println(it.key.getName() + ": " + it.key.getState())
for (stackTraceElement in it.key.getStackTrace()) {
System.err.println("\t" + stackTraceElement)
}
}
}
override fun failed(e: Throwable?, description: Description?) {
super.failed(e, description)
dumpThreads()
}
}
您可以在测试中使用:
@Rule
@JvmField
var dumpThreadRule = DumpThreadRule()
但是,就我而言,所有线程要么是“RUNNABLE”,“TIMED_WAITING”要么是“WAITING”。他们都没有“运行”。因此我的 UI 线程应该是“IDLE”不是?
您可以创建线程转储来查看是什么阻止了您的应用程序。有时,当您创建多个线程转储时它会有所帮助。
我在将大量 UI 测试转移到 robolectric 后看到了这一点。 但我并不总是看到它。 我相当确定在我的情况下发生了什么,是某些东西正在泄漏内存,过了一会儿,JVM 陷入了困境,然后事情发生得非常缓慢,然后就超出了超时时间。 如果我使用非常低的值(例如 10)调用 setForkEvery,则问题永远不会发生。 如果我不全部设置,我将无法在没有 OutMemoryException 的情况下运行所有测试。 值在 50 到 100 之间时,要么成功,要么失败。 我的任务是找出导致泄漏/速度变慢的原因。 当不经常重新启动 JVM 时,测试运行得更快,但我希望测试能够完成。 我怀疑参数化测试是问题的根源。