espresso 2.2.2と、Runner/Rules 0.5が公開されていた。Silent releaseぽい。
https://google.github.io/android-testing-support-library/downloads/release-notes/index.html
更新情報の中で気になったところをメモ。sampleとかも書かれていないので、コードを直接追ってみました。コードまで追ってみた結果としては、 withResourceName だけ気にしていれば良いかなという感じ。
New feature
- Added checks for enabled animations and transitions
- New ViewMatcher API: withResourceName
notable change
- ActivityTestRule, UiThreadTestRule, IntentsTestRule and ServiceTestRule are out of beta.
dive into code
withResourceName
コードを見た感じだと以下。 withResourceName は resource idではなく、name属性から要素を特定できるっぽい。
android/support/test/espresso/matcher/ViewMatchers.java
| // android/support/test/espresso/matcher/ViewMatchers.java | |
| /** | |
| * Returns a matcher that matches {@link View}s based on resource id names, | |
| * (for instance, channel_avatar). | |
| * | |
| * @param name the resource id name | |
| */ | |
| public static Matcher<View> withResourceName(String name) { | |
| return withResourceName(is(name)); | |
| } | |
| /** | |
| * Returns a matcher that matches {@link View}s based on resource id names, | |
| * (for instance, channel_avatar). | |
| * | |
| * @param stringMatcher a Matcher for resource id names | |
| */ | |
| public static Matcher<View> withResourceName(final Matcher<String> stringMatcher) { | |
| checkNotNull(stringMatcher); | |
| return new TypeSafeMatcher<View>() { | |
| @Override | |
| public void describeTo(Description description) { | |
| description.appendText("with res-name that "); | |
| stringMatcher.describeTo(description); | |
| } | |
| @Override | |
| public boolean matchesSafely(View view) { | |
| if (view.getId() == -1 || view.getResources() == null) { | |
| return false; | |
| } | |
| try { | |
| return stringMatcher.matches(view.getResources().getResourceEntryName(view.getId())); | |
| } catch (Resources.NotFoundException ignore) { | |
| return false; | |
| } | |
| } | |
| }; | |
| } |
animation
Added checks for enabled animations and transitions に関しては、テストがアニメーションの設定が起因で失敗した時には
"Animations or transitions are enabled on the target device.\n" + "For more info check: http://goo.gl/qVu1yV\n\n");
の失敗が表示されるらしい。ここは、以下のHandlerメソッドに内包されている。
http://developer.android.com/reference/android/support/test/espresso/base/DefaultFailureHandler.html
ちなみに、http://goo.gl/qVu1yV のリンク先には以下があるので、結局は端末のアニメーションをOFFにしてね、という表示をするだけぽい。
On your device, under Settings->Developer options disable the following 3 settings:
– Window animation scale
– Transition animation scale
– Animator duration scale
この判定箇所のprivateコードが眠っている箇所は以下。
android/support/test/espresso/base/DefaultFailureHandler.java
| // android/support/test/espresso/base/DefaultFailureHandler.java | |
| .... | |
| @Override | |
| public void handle(Throwable error, Matcher<View> viewMatcher) { | |
| if (error instanceof EspressoException || error instanceof AssertionFailedError | |
| || error instanceof AssertionError) { | |
| throw propagate(getUserFriendlyError(error, viewMatcher)); | |
| } else { | |
| throw propagate(error); | |
| } | |
| } | |
| /** | |
| * When the error is coming from espresso, it is more user friendly to: | |
| * 1. propagate assertions as assertions | |
| * 2. swap the stack trace of the error to that of current thread (which will show | |
| * directly where the actual problem is) | |
| */ | |
| private Throwable getUserFriendlyError(Throwable error, Matcher<View> viewMatcher) { | |
| if (error instanceof PerformException) { | |
| StringBuilder sb = new StringBuilder(); | |
| if (!isAnimationAndTransitionDisabled(appContext)) { | |
| sb.append( | |
| "Animations or transitions are enabled on the target device.\n" | |
| + "For more info check: http://goo.gl/qVu1yV\n\n"); | |
| } | |
| sb.append(viewMatcher.toString()); | |
| // Re-throw the exception with the viewMatcher (used to locate the view) as the view | |
| // description (makes the error more readable). The reason we do this here: not all creators | |
| // of PerformException have access to the viewMatcher. | |
| throw new PerformException.Builder() | |
| .from((PerformException) error) | |
| .withViewDescription(sb.toString()) | |
| .build(); | |
| } | |
| if (error instanceof AssertionError) { | |
| // reports Failure instead of Error. | |
| // assertThat(...) throws an AssertionFailedError. | |
| error = new AssertionFailedWithCauseError(error.getMessage(), error); | |
| } | |
| error.setStackTrace(Thread.currentThread().getStackTrace()); | |
| return error; | |
| } | |
| .... | |
| /** | |
| * Checks whether animations and transitions are disabled on the current device. | |
| * | |
| * @param context The target's context. | |
| * | |
| * @return <code>true</code> if animations or transitions are disabled, else <code>false</code>. | |
| * | |
| */ | |
| private static boolean isAnimationAndTransitionDisabled(Context context) { | |
| ContentResolver resolver = context.getContentResolver(); | |
| boolean isTransitionAnimationDisabled = isEqualToZero(getTransitionAnimationScale(resolver)); | |
| boolean isWindowAnimationDisabled = isEqualToZero(getWindowAnimationScale(resolver)); | |
| boolean isAnimatorDisabled = isEqualToZero(getAnimatorDurationScale(resolver)); | |
| return isTransitionAnimationDisabled && isWindowAnimationDisabled && isAnimatorDisabled; | |
| } | |
| private static boolean isEqualToZero(float value) { | |
| return Float.compare(Math.abs(value), 0.0f) == 0; | |
| } | |
| private static float getTransitionAnimationScale(ContentResolver resolver) { | |
| return getSetting(resolver, Settings.Global.TRANSITION_ANIMATION_SCALE, | |
| Settings.System.TRANSITION_ANIMATION_SCALE); | |
| } | |
| private static float getWindowAnimationScale(ContentResolver resolver) { | |
| return getSetting(resolver, Settings.Global.WINDOW_ANIMATION_SCALE, | |
| Settings.System.WINDOW_ANIMATION_SCALE); | |
| } | |
| private static float getAnimatorDurationScale(ContentResolver resolver) { | |
| if (isJellyBeanMR1OrHigher()) { | |
| return getSetting(resolver, Settings.Global.ANIMATOR_DURATION_SCALE, | |
| Settings.System.ANIMATOR_DURATION_SCALE); | |
| } | |
| return 0f; | |
| } | |
| /** | |
| * Compatibility wrapper for obtaining animation related settings. | |
| * | |
| * Gets an animation specific setting regardless of the API level the tests are running on. | |
| * | |
| * @param resolver The content resolver to use. | |
| * @param current The setting name to use on {@link JELLY_BEAN_MR1} and above. | |
| @see #getGlobalSetting(ContentResolver, String) | |
| * @param deprecated The setting name to use up to {@link JELLY_BEAN_MR1}. | |
| @see #getSystemSetting(ContentResolver, String) | |
| */ | |
| private static float getSetting(ContentResolver resolver, String current, String deprecated) { | |
| if (isJellyBeanMR1OrHigher()) { | |
| return getGlobalSetting(resolver, current); | |
| } else { | |
| return getSystemSetting(resolver, deprecated); | |
| } | |
| } |
結局は、アニメーションをEspressoのsetupとかでOFFにはできないのですね。まだadbコマンドだけか。