web-dev-qa-db-fra.com

Utiliser Espresso pour tester les changements pouvant être dessinés

Les tests Espresso sont nouveaux pour moi, mais il ne semble pas qu'il soit possible de tester les modifications pouvant être effectuées.

J'ai un tutoriel qui s'appelle ImageViewDrawablediaporama 'niché dans' un TextViewsemi-transparent. Dans mes tests, je veux m'assurer que lorsque le bouton suivant est enfoncé, le Drawableapproprié a été inséré dans le ImageViewdu tutoriel.

Il n'y a pas de nom par défaut Matcherpour vérifier Drawablename__s. Je me suis donc mis à écrire le mien en utilisant https://stackoverflow.com/a/28785178/981242 . Malheureusement, comme il n’existe aucun moyen de récupérer l’identifiant d’un ImageViewname __ actif Drawablename__, je ne peux pas terminer l’implémentation matchesSafely().

Cela ne peut pas être le seul cas d'utilisation pour tester les Drawablename__s actifs. Quel est l'outil que les gens utilisent normalement pour de telles situations?

12
nukeforum

Je préfère ne pas comparer les images bitmap et suivre les conseils de cette réponse: https://stackoverflow.com/a/14474954/1396068

Lorsque vous définissez le dessin de la vue d'image, stockez également l'ID pouvant être dessiné dans sa balise avec setTag(R.drawable.your_drawable). Ensuite, utilisez les adaptateurs withTagValue(equalTo(R.drawable.your_drawable)) de Espresso pour vérifier la balise correcte.

10
Fabian Streitel

veuillez vérifier ce tutoriel que j'ai trouvé. semble fonctionner plutôt bien https://medium.com/@dbottillo/Android-ui-test-espresso-matcher-for-imageview-1a28c832626f#.4snjg8frw

Voici le résumé pour copier les pâtes ;-)

public class DrawableMatcher extends TypeSafeMatcher<View> {

    private final int expectedId;
    String resourceName;

    public DrawableMatcher(int expectedId) {
        super(View.class);
        this.expectedId = expectedId;
    }

    @Override
    protected boolean matchesSafely(View target) {
        if (!(target instanceof ImageView)){
            return false;
        }
        ImageView imageView = (ImageView) target;
        if (expectedId < 0){
            return imageView.getDrawable() == null;
        }
        Resources resources = target.getContext().getResources();
        Drawable expectedDrawable = resources.getDrawable(expectedId);
        resourceName = resources.getResourceEntryName(expectedId);

        if (expectedDrawable == null) {
            return false;
        }

        Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
        Bitmap otherBitmap = ((BitmapDrawable) expectedDrawable).getBitmap();
        return bitmap.sameAs(otherBitmap);
    }


    @Override
    public void describeTo(Description description) {
        description.appendText("with drawable from resource id: ");
        description.appendValue(expectedId);
        if (resourceName != null) {
            description.appendText("[");
            description.appendText(resourceName);
            description.appendText("]");
        }
    }
}

Sachez que cela ne fonctionne que lorsque votre Drawable est un BitmapDrawable. Si vous avez également VectorDrawable ou une autre Drawable, vous devez vérifier cela (imageView.getDrawable() instanceOf XXXDrawable) et en extraire le bitmap. Sauf que vous avez une sorte de dessin simple où vous avez seulement une couleur ou vous pouvez comparer.

Pour obtenir le bitmap d'un VectorDrawable, par exemple, vous devez dessiner le VectorDrawable sur un canevas et l'enregistrer dans un bitmap (j'ai eu quelques problèmes lorsque le VectorDrawable a été teinté). Si vous avez un StateListDrawable, vous pouvez obtenir le Drawable de l'état sélectionné et répéter votre cascade if .Of. Pour les autres types Drawable, je n'ai aucune expérience, désolé!

9
wolle

Il y a qu'un Gist qui contient withBackground(), withCompoundDrawable(), withImageDrawable() matchers de Frankie Sardo . Tous les crédits à lui. 

Et en ce qui concerne les identifiants d’image - vous pouvez taper R.drawable.image_name, l’id du dessinable sera alors récupéré automatiquement.

6
denys

Basé sur l'aide de @wolle et de @ FreewheelNat, pour comparer (Vector) Drawable: 

public static Matcher<View> withDrawableId(@DrawableRes final int id) {
    return new DrawableMatcher(id);
}


public static class DrawableMatcher extends TypeSafeMatcher<View> {

    private final int expectedId;
    private String resourceName;

    public DrawableMatcher(@DrawableRes int expectedId) {
        super(View.class);
        this.expectedId = expectedId;
    }

    @Override
    protected boolean matchesSafely(View target) {
        if (!(target instanceof ImageView)) {
            return false;
        }
        ImageView imageView = (ImageView) target;
        if (expectedId < 0) {
            return imageView.getDrawable() == null;
        }
        Resources resources = target.getContext().getResources();
        Drawable expectedDrawable = resources.getDrawable(expectedId);
        resourceName = resources.getResourceEntryName(expectedId);
        if (expectedDrawable != null && expectedDrawable.getConstantState() != null) {
            return expectedDrawable.getConstantState().equals(
                    imageView.getDrawable().getConstantState()
            );
        } else {
            return false;
        }
    }


    @Override
    public void describeTo(Description description) {
        description.appendText("with drawable from resource id: ");
        description.appendValue(expectedId);
        if (resourceName != null) {
            description.appendText("[");
            description.appendText(resourceName);
            description.appendText("]");
        }
    }
}
2
drakeet

J'accepte la réponse de @wolle comme valide, mais je voudrais admettre que, même pour Java, il pourrait même être plus simple que cela. Il peut être converti en un static function (ou en une companion dans Kotlin) et également nettoyer un code obsolète

Quoi qu'il en soit, la solution pour Kotlin compactée par le code et non obsolète serait la suivante:

    fun drawableIsCorrect(@DrawableRes drawableResId: Int): Matcher<View> {
        return object : TypeSafeMatcher<View>() {
            override fun describeTo(description: Description) {
                description.appendText("with drawable from resource id: ")
                description.appendValue(drawableResId)
            }

            override fun matchesSafely(target: View?): Boolean {
                if (target !is ImageView) {
                    return false
                }
                if (drawableResId < 0) {
                    return target.drawable == null
                }
                val expectedDrawable = ContextCompat.getDrawable(target.context, drawableResId)
                        ?: return false

                val bitmap = (target.drawable as BitmapDrawable).bitmap
                val otherBitmap = (expectedDrawable as BitmapDrawable).bitmap
                return bitmap.sameAs(otherBitmap)
            }
        }
    }

22 lignes contre 44, hein? 

0
Rafael Ruiz Muñoz

J'ai déjà répondu sur le même sujet ici: Obtenir l'ID d'un dessinable dans ImageView . L'approche consiste à marquer une vue avec un identifiant de ressource spécifié dans la variable personnalisée LayoutInflater. L'ensemble du processus est automatisé par une simple bibliothèque TagView . C'est particulièrement utile pour le test Espresso car il n'est pas nécessaire de baliser manuellement toutes les vues de votre projet. En fait, vous n'avez rien à changer, sauf que vous définissez des éléments dessinables au moment de l'exécution. Dans ce cas, vous devez examiner Tagging in runtime section.

En conséquence, vous pouvez comparer deux drawables uniquement par leurs identifiants:

onView(withId(R.id.imageview)).check(assertTagKeyValue(
               ViewTag.IMAGEVIEW_SRC.id, Android.R.drawable.ic_media_play));

L'assertion Espresso personnalisée assertTagKeyValue est disponible ici

0
Bogdan Kornev