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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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コマンドだけか。