首页 > 解决方案 > 使用 Robolectric 进行单元测试时如何修复 androidx.test.espresso.PerformException?

问题描述

我正在本地 JVM 上使用 Robolectric 进行单元测试。

当我单击一个按钮时,我得到 androidx.test.espresso.PerformException: Error execution'single click - At Coordinates。

    @Test
    public void givenVerificationCodeEmpty_whenUserPressVerify_thenErrorMustAppear() {

        ActivityScenario.launch(VerifyAccountActivity.class);
        onView(withId(R.id.verify_account_verify_button)).perform(click());
        String error = context.getString(R.string.error_verification_code_empty);
        onView(withId(R.id.verify_code_input)).check(matches(hasErrorText(error)));
    }

我正在使用 Android Studio 3.3.2 和 Robolectric 4.2。

绑定到按钮的动作涉及 view.requestFocus() 如代码所示

private void verify() {
        mVerificationCodeView.setError(null);
        String code = mVerificationCodeView.getText().toString();
        if(TextUtils.isEmpty(code)) {
            mVerificationCodeView.setError(getResources().getString(R.string.error_verification_code_empty));
            mVerificationCodeView.requestFocus();
        } else {
            showProgress(true);
        }
    }

这是完整的错误堆栈

androidx.test.espresso.PerformException: Error performing 'single click - At Coordinates: 159, 372 and precision: 16, 16' on view 'with id: com.syriail.aladdin:id/verify_account_verify_button'.

    at androidx.test.espresso.PerformException$Builder.build(PerformException.java:82)
    at androidx.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:79)
    at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:51)
    at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:312)
    at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:173)
    at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:114)
    at com.syriail.aladdin.useraccount.VerifyAccountUnitTest.givenVerificationCodeEmpty_whenUserPressVerify_thenErrorMustAppear(VerifyAccountUnitTest.java:83)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:256)
    at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:89)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
    at android.widget.PopupWindow.getDecorViewLayoutParams(PopupWindow.java:2201)
    at android.widget.PopupWindow.update(PopupWindow.java:2257)
    at android.widget.PopupWindow.update(PopupWindow.java:2235)
    at android.widget.Editor.setFrame(Editor.java:868)
    at android.widget.TextView.setFrame(TextView.java:6481)
    at android.view.View.layout(View.java:20669)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
    at android.view.View.layout(View.java:20672)
    at android.view.ViewGroup.layout(ViewGroup.java:6194)
    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)
    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)
    at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)
    at com.google.android.material.textfield.TextInputLayout.onLayout(TextInputLayout.java:1885)
    at android.view.View.layout(View.java:20672)
    at android.view.ViewGroup.layout(ViewGroup.java:6194)
    at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
    at android.view.View.layout(View.java:20672)
    at android.view.ViewGroup.layout(ViewGroup.java:6194)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
    at android.view.View.layout(View.java:20672)
    at android.view.ViewGroup.layout(ViewGroup.java:6194)
    at androidx.appcompat.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:444)
    at android.view.View.layout(View.java:20672)
    at android.view.ViewGroup.layout(ViewGroup.java:6194)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
    at android.view.View.layout(View.java:20672)
    at android.view.ViewGroup.layout(ViewGroup.java:6194)
    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1812)
    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1656)
    at android.widget.LinearLayout.onLayout(LinearLayout.java:1565)
    at android.view.View.layout(View.java:20672)
    at android.view.ViewGroup.layout(ViewGroup.java:6194)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:323)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:261)
    at com.android.internal.policy.DecorView.onLayout(DecorView.java:753)
    at android.view.View.layout(View.java:20672)
    at android.view.ViewGroup.layout(ViewGroup.java:6194)
    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2792)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2319)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1460)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7183)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at org.robolectric.shadows.ShadowMessageQueue.dispatchMessage(ShadowMessageQueue.java:148)
    at org.robolectric.shadows.ShadowMessageQueue.access$200(ShadowMessageQueue.java:38)
    at org.robolectric.shadows.ShadowMessageQueue$1.run(ShadowMessageQueue.java:126)
    at org.robolectric.util.Scheduler$ScheduledRunnable.run(Scheduler.java:386)
    at org.robolectric.util.Scheduler.runOneTask(Scheduler.java:278)
    at org.robolectric.util.Scheduler.advanceTo(Scheduler.java:260)
    at org.robolectric.util.Scheduler.advanceBy(Scheduler.java:243)
    at org.robolectric.util.Scheduler.advanceBy(Scheduler.java:233)
    at org.robolectric.util.Scheduler.setIdleState(Scheduler.java:88)
    at org.robolectric.util.Scheduler.unPause(Scheduler.java:123)
    at org.robolectric.shadows.ShadowLooper.unPause(ShadowLooper.java:347)
    at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:398)
    at org.robolectric.shadows.ShadowViewGroup.addView(ShadowViewGroup.java:27)
    at android.view.ViewGroup.addView(ViewGroup.java)
    at android.view.ViewGroup.addView(ViewGroup.java:4822)
    at android.widget.PopupWindow.createDecorView(PopupWindow.java:1409)
    at android.widget.PopupWindow.preparePopup(PopupWindow.java:1356)
    at android.widget.PopupWindow.showAsDropDown(PopupWindow.java:1277)
    at android.widget.Editor.showError(Editor.java:565)
    at android.widget.Editor.onFocusChanged(Editor.java:1296)
    at android.widget.TextView.onFocusChanged(TextView.java:9991)
    at android.view.View.handleFocusGainInternal(View.java:6934)
    at android.view.View.requestFocusNoSearch(View.java:11546)
    at android.view.View.requestFocus(View.java:11520)
    at android.view.View.requestFocus(View.java:11487)
    at android.view.View.requestFocus(View.java:11429)
    at com.syriail.aladdin.VerifyAccountActivity.verify(VerifyAccountActivity.java:107)
    at com.syriail.aladdin.VerifyAccountActivity.access$000(VerifyAccountActivity.java:29)
    at com.syriail.aladdin.VerifyAccountActivity$1.onClick(VerifyAccountActivity.java:63)
    at android.view.View.performClick(View.java:6597)
    at android.view.View.performClickInternal(View.java:6574)
    at android.view.View.access$3100(View.java:778)
    at android.view.View$PerformClick.run(View.java:25885)
    at org.robolectric.util.Scheduler.runOrQueueRunnable(Scheduler.java:338)
    at org.robolectric.util.Scheduler.postDelayed(Scheduler.java:162)
    at org.robolectric.util.Scheduler.post(Scheduler.java:141)
    at org.robolectric.shadows.ShadowView.post(ShadowView.java:365)
    at android.view.View.post(View.java)
    at android.view.View.onTouchEvent(View.java:13787)
    at android.view.View$GeneratedProxy/243455390.onTouchEvent(Unknown Source)
    at org.robolectric.shadows.ShadowView.onTouchEvent(ShadowView.java:219)
    at android.view.View.onTouchEvent(View.java)
    at android.widget.TextView.onTouchEvent(TextView.java:10064)
    at android.view.View.dispatchTouchEvent(View.java:12513)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3052)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
    at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:440)
    at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1830)
    at android.app.Activity.dispatchTouchEvent(Activity.java:3400)
    at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
    at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:398)
    at org.robolectric.android.internal.LocalUiController.injectMotionEvent(LocalUiController.java:41)
    at androidx.test.espresso.base.UiControllerModule$EspressoUiControllerAdapter.injectMotionEvent(UiControllerModule.java:64)
    at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:160)
    at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:137)
    at androidx.test.espresso.action.Tap.sendSingleTap(Tap.java:170)
    at androidx.test.espresso.action.Tap.access$100(Tap.java:31)
    at androidx.test.espresso.action.Tap$1.sendTap(Tap.java:47)
    at androidx.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:136)
    at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:356)
    at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:248)
    at androidx.test.espresso.ViewInteraction.access$100(ViewInteraction.java:63)
    at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:153)
    at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:150)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at org.robolectric.shadows.ShadowMessageQueue.dispatchMessage(ShadowMessageQueue.java:148)
    at org.robolectric.shadows.ShadowMessageQueue.access$200(ShadowMessageQueue.java:38)
    at org.robolectric.shadows.ShadowMessageQueue$1.run(ShadowMessageQueue.java:126)
    at org.robolectric.util.Scheduler.runOrQueueRunnable(Scheduler.java:338)
    at org.robolectric.util.Scheduler.postDelayed(Scheduler.java:162)
    at org.robolectric.util.Scheduler.postDelayed(Scheduler.java:151)
    at org.robolectric.shadows.ShadowMessageQueue.enqueueMessage(ShadowMessageQueue.java:133)
    at android.os.MessageQueue.enqueueMessage(MessageQueue.java)
    at android.os.Handler.enqueueMessage(Handler.java:745)
    at android.os.Handler.sendMessageAtTime(Handler.java:697)
    at android.os.Handler.sendMessageDelayed(Handler.java:667)
    at android.os.Handler.post(Handler.java:395)
    at androidx.test.espresso.base.BaseLayerModule$1.execute(BaseLayerModule.java:92)
    at androidx.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:303)
    at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:161)
    at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:114)
    at com.syriail.aladdin.useraccount.VerifyAccountUnitTest.givenVerificationCodeEmpty_whenUserPressVerify_thenErrorMustAppear(VerifyAccountUnitTest.java:83)

注意 NullPointerException。我不明白为什么!

当摆脱以下代码行时,它按预期工作。

mVerificationCodeView.requestFocus();

这个问题只发生在我在本地 JVM 上运行测试时,但是当我使用 androidTest,即在真实设备或模拟器上运行测试时,它可以按预期工作而无需删除上述代码行。像下面的测试

    @Test
    public void givenVerificationCodeEmpty_whenUserPressVerify_thenErrorMustAppear() {
        onView(withId(R.id.verify_account_verify_button)).perform(click());
        String error = activityTestRule.getActivity().getString(R.string.error_verification_code_empty);
        onView(withId(R.id.verify_code_input)).check(matches(hasErrorText(error)));
    }

有关我使用的库的更多信息,这是我的 Gradle 的一部分

// JUnit 4 framework
    testImplementation 'junit:junit:4.12'

    /**
     * Unit Testing
     */

    // Core library
    testImplementation 'androidx.test:core:1.0.0'

    // AndroidJUnitRunner and JUnit Rules
    testImplementation 'androidx.test.ext:junit:1.1.0'

    // Robolectric
    testImplementation 'org.robolectric:robolectric:4.2'

    // Espresso
    testImplementation 'androidx.test.espresso:espresso-core:3.1.0'

标签: androidandroid-espressorobolectric

解决方案


推荐阅读