Espresso:如何忽略/处理AppNotIdleException以实现长时间UI测试
Hey there! Let's work through this Espresso AppNotIdleException issue you're dealing with—your use case with physical ticket printing and 24/7 UI testing has some unique constraints, so let's break down the most practical fixes:
First, a quick note: Espresso's idle check is core to its reliability, so we'll prioritize solutions that work with its design rather than against it. But we'll also cover emergency options if you need them.
1. Extend Espresso's Idle Timeout (Quick Win)
Instead of disabling the idle check entirely, you can simply give it more time to wait for slow operations like ticket printing. This keeps Espresso's safety checks intact while accommodating your use case.
Add this setup code to your test class:
@Before fun setupTest() { // Adjust the time based on how long your ticket printing typically takes IdlingPolicies.setMasterPolicyTimeout(3, TimeUnit.MINUTES) IdlingPolicies.setIdlingResourceTimeout(3, TimeUnit.MINUTES) }
This replaces the default 60-second limit with a longer window, so Espresso won't throw an exception while waiting for the print job to finish.
2. Use a Custom IdlingResource (Best Practice)
The most robust fix is to tell Espresso exactly when your ticket printing operation is complete. This way, it won't proceed until the app is truly idle, no matter how long the print takes.
Here's how to implement it:
Step 1: Create the IdlingResource
class PrintingIdlingResource : IdlingResource { private var callback: ResourceCallback? = null private var isPrintInProgress = false // Call this right before triggering a print job fun markPrintAsStarted() { isPrintInProgress = true } // Call this when you get confirmation the print is done (e.g., from a callback) fun markPrintAsFinished() { isPrintInProgress = false callback?.onTransitionToIdle() } override fun getName() = "PrintingIdlingResource" override fun isIdleNow() = !isPrintInProgress override fun registerIdleTransitionCallback(callback: ResourceCallback?) { this.callback = callback } }
Step 2: Register It in Your Tests
private val printingIdlingResource = PrintingIdlingResource() @Before fun setup() { Espresso.registerIdlingResources(printingIdlingResource) } @After fun teardown() { Espresso.unregisterIdlingResources(printingIdlingResource) } // In your test logic: printingIdlingResource.markPrintAsStarted() // Trigger the print action here // When your app signals the print is complete (e.g., via a listener), call: printingIdlingResource.markPrintAsFinished()
This aligns perfectly with Espresso's design and eliminates timeout issues entirely.
3. Temporarily Bypass Idle Checks (Emergency Only)
If you can't implement an IdlingResource for some reason, you can skip the idle check for specific operations. Note: This can make tests unstable, so use it sparingly.
Use this to wait without enforcing idle state:
Espresso.onView(ViewMatchers.isRoot()).perform(object : ViewAction { override fun getConstraints() = ViewMatchers.isRoot() override fun getDescription() = "Wait for print without idle check" override fun perform(uiController: UiController, view: View) { // Wait for X milliseconds (adjust based on your print time) uiController.loopMainThreadForAtLeast(15000) } })
This is better than Thread.sleep() because it keeps the main thread alive, avoiding ANRs.
4. Reduce Unnecessary Ticket Printing (Root Cause Fix)
To avoid the problem altogether, consider optimizing your test flow:
- Add a test mode to your app that skips physical ticket printing (e.g., mock the print service to just log instead of printing)
- Reuse test data or auto-void printed tickets after tests
- Isolate ticket printing tests to run less frequently in your 24/7 suite
内容的提问来源于stack exchange,提问作者Richardweber32

