web-dev-qa-db-fra.com

Espresso: retourne un booléen si la vue existe

J'essaie de vérifier si une vue est affichée avec Espresso. Voici un pseudo-code pour montrer ce que j'essaye:

if (!Espresso.onView(withId(R.id.someID)).check(doesNotExist()){
   // then do something
 } else {
   // do nothing, or what have you
 }

Mais mon problème est .check(doesNotExist()) ne retourne pas booléen. C'est juste une affirmation. Avec UiAutomator, j'ai pu faire quelque chose comme ceci:

 if (UiAutomator.getbyId(SomeId).exists()){
      .....
   }
33
Chad Bingham

La logique conditionnelle dans les tests est indésirable . Dans cet esprit, l’API d’Espresso a été conçu pour guider l’auteur du test (en étant explicite avec les actions de test et les assertions).

Cela dit, vous pouvez toujours obtenir ce qui précède en implémentant votre propre ViewAction et en capturant le contrôle isDisplayed (dans la méthode perform) dans un AtomicBoolean.

Une autre option moins élégante - attrapez l'exception qui est levée par échec:

    try {
        onView(withText("my button")).check(matches(isDisplayed()));
        //view is displayed logic
    } catch (NoMatchingViewException e) {
        //view not displayed logic
    }
64
ValeraZakharov

Je pense que pour imiter UIAutomator, vous pouvez le faire:
( Bien que, je suggère de repenser votre approche pour n’avoir aucune condition. )

ViewInteraction view = onView(withBlah(...)); // supports .inRoot(...) as well
if (exists(view)) {
     view.perform(...);
}

@CheckResult
public static boolean exists(ViewInteraction interaction) {
    try {
        interaction.perform(new ViewAction() {
            @Override public Matcher<View> getConstraints() {
                return any(View.class);
            }
            @Override public String getDescription() {
                return "check for existence";
            }
            @Override public void perform(UiController uiController, View view) {
                // no op, if this is run, then the execution will continue after .perform(...)
            }
        });
        return true;
    } catch (AmbiguousViewMatcherException ex) {
        // if there's any interaction later with the same matcher, that'll fail anyway
        return true; // we found more than one
    } catch (NoMatchingViewException ex) {
        return false;
    } catch (NoMatchingRootException ex) {
        // optional depending on what you think "exists" means
        return false;
    }
}

De plus, exists sans branchement peut être implémenté très simplement:

onView(withBlah()).check(exists()); // the opposite of doesNotExist()

public static ViewAssertion exists() {
    return matches(anything());
}

Quoiqu'il en soit, la plupart du temps, il vaut la peine de vérifier matches(isDisplayed()).

9
TWiStErRob

Vous vérifiez également avec le code ci-dessous. Si view est affiché, il cliquera sinon il passera.

onView(withText("OK")).withFailureHandler(new FailureHandler() {
        @Override
        public void handle(Throwable error, Matcher<View> viewMatcher){
        }
    }).check(matches(isDisplayed())).perform(customClick());
8
Dhiren Mudgil

Nous avons besoin de cette fonctionnalité et j'ai fini par l'implémenter ci-dessous:

https://github.com/marcosdiez/espresso_clone

if(onView(withText("click OK to Continue")).exists()){ 
    doSomething(); 
} else { 
   doSomethingElse(); 
}

J'espère que cela vous sera utile.

8
user3411862

Basé sur la réponse de Dhiren Mudgil, j’ai écrit la méthode suivante: 

public static boolean viewIsDisplayed(int viewId)
{
    final boolean[] isDisplayed = {true};
    onView(withId(viewId)).withFailureHandler(new FailureHandler()
    {
        @Override
        public void handle(Throwable error, Matcher<View> viewMatcher)
        {
            isDisplayed[0] = false;
        }
    }).check(matches(isDisplayed()));
    return isDisplayed[0];
}

J'utilise ceci pour aider à déterminer quelle vue dans ViewFlipper est actuellement affichée.

4
trooper

Cela fait longtemps que ce problème n’a pas été annoncé, mais comme c’est l’un des grands succès de Google lorsqu’il cherche à s’assurer de la présence d’une vue, avant de faire quoi que ce soit dans Espresso, j’aimerais partager mes connaissances de base. façon de gérer cela.

1: Commencez par écrire une extension à ViewInteraction:

fun ViewInteraction.exists(): Boolean {
val viewExists = AtomicReference<Boolean>()

this.perform(object : ViewAction {
    override fun perform(uiController: UiController?, view: View?) {
        viewExists.set(view != null)
    }

    override fun getConstraints(): Matcher<View>? {
        return Matchers.allOf(ViewMatchers.withEffectiveVisibility(
                ViewMatchers.Visibility.VISIBLE),
                ViewMatchers.isAssignableFrom(View::class.Java))
    }

    override fun getDescription(): String {
        return "check if view exists"
    }
})
return viewExists.get()

}

2: Créez une méthode d’aide simple dans votre classe de base (à utiliser dans toutes les classes de test):

fun viewExists(id: Int): Boolean {
    return try {
        onView(withId(id)).exists()
    } catch (e: RuntimeException) {
        false
    }
}

Avec cela, vous obtenez soit true ou false à partir de onView(withId(id)).exists(), ou vous attrapez en toute sécurité l’exception RuntimeException et renvoyez false.

Normalement, une simple vérification de .exists() serait suffisante, mais dans certains cas, comme lorsque vous supprimez des éléments ListView jusqu'à ce qu'il ne reste plus rien -> lorsque le dernier élément est supprimé, il se peut que ListView ne soit plus présent, puis une exception est levée lors de la tentative vérifier s'il existe. 

3: Avec l'implémentation ci-dessus, il est prudent de vérifier si une vue existe, car la variable RuntimeException est gérée correctement en arrière-plan:

if(viewExists(R.id.something)) {
    //do something
}
//do something else
1
Morten Løvborg