Search code examples
android-espressoaccessibility

Working example of suppressing failures when accessibility testing in Android Espresso?


I have my test setup like this:

Base class:

public class AccessibilityBase {

    public static AccessibilityValidator accessibilityValidator;

    @BeforeClass
    public static void enableAccessibilityChecks() {
        accessibilityValidator = AccessibilityChecks.enable();
        accessibilityValidator.setThrowExceptionForErrors(true);
    }
    ...

Test class:

public class LoginAccessibilityTest extends AccessibilityBase {

    @Rule
    public ActivityTestRule<LoginActivity> activityTestRule = new ActivityTestRule<>(LoginActivity.class, true, false);

    private static Matcher<? super AccessibilityViewCheckResult> suppress = Matchers.anyOf(
            allOf(matchesCheckNames(is("TouchTargetSizeViewCheck")),
                    matchesViews(withId(R.id.main_button_login)))
    );

    @BeforeClass
    public static void enableAccessibilityChecks() {
        accessibilityValidator = AccessibilityChecks.enable();
        accessibilityValidator.setSuppressingResultMatcher(suppress);
    }

    @Test
    public void testLoginActivity() {
        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        Intent intent = new Intent(context, LoginActivity.class);
        LoginActivity loginActivity = activityTestRule.launchActivity(intent);

        // UPDATE: Had to pass suppress through here to the base class
        runAllTests(loginActivity, suppress);
    }
}

And yet, when I run this it still fails with this error message:

java.lang.AssertionError: Button app:id/main_button_login: View falls below the minimum recommended size for touch targets. Minimum touch target size is 48x48dp. Actual size is 446.0x45.0dp (screen density is 3.0).

UPDATE: The key was in my runAllTests method. I've fixed it by passing the suppress matcher in the test class to runAllTests()

private static void runTests(Activity activity, Set<AccessibilityViewHierarchyCheck> checks, Matcher<? super AccessibilityViewCheckResult> suppress) {
        List<AccessibilityViewCheckResult> results = new LinkedList<>();
        View rootView = activity.findViewById(android.R.id.content);
        for (AccessibilityViewHierarchyCheck check : checks) {
            try {
                results.addAll(check.runCheckOnViewHierarchy(rootView));
            } catch (Exception ex) {

            }
        }
        if (suppress != null) {
            AccessibilityCheckResultUtils.suppressMatchingResults(results, suppress);
        }

That last bit was necessary even though I had it set in the test's @BeforeClass.


Solution

  • There are two answers depending on which libraries are being used. As a note, the first example here is deprecated, but works fine:

    import com.google.android.apps.common.testing.accessibility.framework.AccessibilityViewCheckResult // <-- deprecated
        ...
        companion object {
            @BeforeClass
            @JvmStatic
            fun enableAccessibilityChecks() {
                val suppress: Matcher<AccessibilityViewCheckResult> = anyOf(
                allOf(matchesCheckNames(`is`("TouchTargetSizeViewCheck")),
                        matchesViews(withId(R.id.small_button))),
                allOf(matchesCheckNames(`is`("DuplicateClickableBoundsViewCheck")),
                        matchesViews(withId(R.id.overlap_button)))
                )
                AccessibilityChecks.enable()
                    .setRunChecksFromRootView(true)
                    .setSuppressingResultMatcher(suppress)
            }
        }
    

    And the newer version:

    import androidx.test.espresso.accessibility.AccessibilityChecks
    import androidx.test.espresso.matcher.ViewMatchers.withId
    import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesCheckNames
    import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesViews
    import org.hamcrest.CoreMatchers.*
    
    ...
    
        init {
            AccessibilityChecks.enable()
                .setRunChecksFromRootView(true)
                .apply {
                    setSuppressingResultMatcher(
                        anyOf(
                            allOf(matchesCheckNames(`is`("TouchTargetSizeViewCheck")),
                                matchesViews(withId(R.id.small_button))),
                            allOf(matchesCheckNames(`is`("DuplicateClickableBoundsViewCheck")),
                                matchesViews(withId(R.id.overlap_button)))
                        )
                    )
                }
        }