Recherche…


Introduction

L'objectif de ce document est de représenter les objectifs et les moyens d'écrire l'interface utilisateur et les tests d'intégration. Espresso et UIAutomator sont fournis par Google, vous devez donc vous concentrer sur ces outils et leurs enveloppes respectives, par exemple Appium, Spoon, etc.

Syntaxe

  • Ressource de ralenti
  • String getName () - Retourne le nom de la ressource inactive (utilisée pour la journalisation et l'idempotence de l'enregistrement).
  • boolean isIdleNow () - Retourne true si la ressource est actuellement inactive.
  • annulez registerIdleTransitionCallback (rappel de IdlingResource.ResourceCallback) - Enregistre le IDlingResource.ResourceCallback donné avec la ressource

Remarques

Règles JUnit:

Comme vous pouvez le voir dans l'exemple de MockWebServer et ActivityTestRule, ils entrent tous dans la catégorie des règles JUnit que vous pouvez créer vous-même et qui doivent ensuite être exécutés pour chaque test définissant son comportement @see: https://github.com/junit-team/junit4/ wiki / règles

Appium

Paramètres

Puisque les paramètres ont des problèmes pour les placer ici jusqu'à ce que le bug de la documentation soit résolu:

Paramètre Détails
Activité de classeClass quelle activité commencer
initialTouchMode si l'activité est mise en mode tactile au démarrage: https://android-developers.blogspot.de/2008/12/touch-mode.html
launchActivity true si l'activité doit être lancée une fois par méthode de test. Il sera lancé avant la première méthode Before et terminé après la dernière méthode After.

Exemple de MockWebServer

Dans le cas où vos activités, fragments et interface utilisateur requièrent un traitement en arrière-plan, une bonne chose à faire est un MockWebServer qui fonctionne localement sur un appareil Android qui apporte un environnement fermé et testable à votre interface utilisateur.

https://github.com/square/okhttp/tree/master/mockwebserver

La première étape consiste à inclure la dépendance graduelle:

testCompile 'com.squareup.okhttp3:mockwebserver:(insert latest version)'

Maintenant, les étapes pour exécuter et utiliser le serveur simulé sont les suivantes:

  • créer un objet serveur de simulation
  • lancez-le à une adresse et à un port spécifiques (généralement localhost: numéro de port)
  • Réponses de mise en file d'attente pour des demandes spécifiques
  • commence le test

Ceci est bien expliqué dans la page github du mockwebserver mais dans notre cas, nous voulons quelque chose de plus agréable et réutilisable pour tous les tests, et les règles de JUnit seront très utiles ici:

/**
 *JUnit  rule that starts and stops a mock web server for test runner
*/
 public class MockServerRule extends UiThreadTestRule {

 private MockWebServer mServer;

 public static final int MOCK_WEBSERVER_PORT = 8000;

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                startServer();
                try {
                    base.evaluate();
                } finally {
                    stopServer();
                }
            }
        };
    }

    /**
     * Returns the started web server instance
     *
     * @return mock server
     */
    public MockWebServer server() {
        return mServer;
    }

    public void startServer() throws IOException, NoSuchAlgorithmException {
        mServer = new MockWebServer();
        try {
            mServer(MOCK_WEBSERVER_PORT);
        } catch (IOException e) {
            throw new IllegalStateException(e,"mock server start issue");
        }
    }

    public void stopServer() {
        try {
            mServer.shutdown();
        } catch (IOException e) {
            Timber.e(e, "mock server shutdown error”);
        }
    }
}

Supposons maintenant que nous avons exactement la même activité que dans l'exemple précédent, mais dans ce cas, lorsque nous appuierons sur le bouton, l'application ira chercher quelque chose sur le réseau, par exemple: https://someapi.com/name

Cela renverrait une chaîne de texte qui serait concaténée dans le texte du snack, par exemple NAME + texte que vous avez tapé.

/**
* Testing of the snackbar activity with networking.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
    //espresso rule which tells which activity to start
    @Rule
    public final ActivityTestRule<SnackbarActivity> mActivityRule = 
        new ActivityTestRule<>(SnackbarActivity.class, true, false);

    //start mock web server
    @Rule
    public final MockServerRule mMockServerRule = new MockServerRule();

    @Override
    public void tearDown() throws Exception {
       //same as previous example
    }
    
    @Override
    public void setUp() throws Exception {
       //same as previous example

       **//IMPORTANT:** point your application to your mockwebserver endpoint e.g.
       MyAppConfig.setEndpointURL("http://localhost:8000");
    }
    
    /**
    *Test methods should always start with "testXYZ" and it is a good idea to 
    *name them after the intent what you want to test
    **/
    @Test
    public void testSnackbarIsShown() {
        //setup mockweb server
        mMockServerRule.server().setDispatcher(getDispatcher());

        mActivityRule.launchActivity(null);
        //check is our text entry displayed and enter some text to it
        String textToType="new snackbar text";
        onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
        //we check is our snackbar showing text from mock webserver plus the one we typed
        onView(withId(R.id.textEntry)).perform(typeText("JazzJackTheRabbit" + textToType));
        //click the button to show the snackbar
        onView(withId(R.id.shownSnackbarBtn)).perform(click());
        //assert that a view with snackbar_id with text which we typed and is displayed
        onView(allOf(withId(android.support.design.R.id.snackbar_text), 
        withText(textToType))) .check(matches(isDisplayed()));
    }
    
     /**
     *creates a mock web server dispatcher with prerecorded requests and responses
     **/
    private Dispatcher getDispatcher() {
        final Dispatcher dispatcher = new Dispatcher() {
            @Override
            public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
                if (request.getPath().equals("/name")){
                    return new MockResponse().setResponseCode(200)
                            .setBody("JazzJackTheRabbit");
                }
                throw new IllegalStateException("no mock set up for " + request.getPath());
            }
        };
        return dispatcher;
    }

Je suggère que vous enveloppiez le répartiteur dans une sorte de générateur afin de pouvoir facilement enchaîner et ajouter de nouvelles réponses pour vos écrans. par exemple

 return newDispatcherBuilder()
            .withSerializedJSONBody("/authenticate", Mocks.getAuthenticationResponse())
            .withSerializedJSONBody("/getUserInfo", Mocks.getUserInfo())
            .withSerializedJSONBody("/checkNotBot", Mocks.checkNotBot());

IdlingResource

La puissance des ressources inactives réside dans le fait qu’il n’est pas nécessaire d’attendre que certains traitements de l’application (mise en réseau, calculs, animations, etc.) se terminent avec sleep() , ce qui entraîne une certaine fragilité et / ou prolonge les tests. La documentation officielle est disponible ici .

la mise en oeuvre

Il y a trois choses à faire lors de l'implémentation de l'interface IdlingResource :

  • getName() - Retourne le nom de votre ressource inactive.
  • isIdleNow() - Vérifie si votre objet xyz, opération, etc. est inactif pour le moment.
  • registerIdleTransitionCallback (callback IdlingResource.ResourceCallback ) - Fournit un rappel que vous devez appeler lorsque votre objet passe au repos.

Vous devez maintenant créer votre propre logique et déterminer à quel moment votre application est inactive et non, car cela dépend de l'application. Vous trouverez ci-dessous un exemple simple, juste pour montrer comment cela fonctionne. Il existe d'autres exemples en ligne, mais la mise en œuvre d'applications spécifiques apporte des implémentations de ressources spécifiques au ralenti.

REMARQUES

  • Il y a eu quelques exemples de Google où ils ont placé IdlingResources dans le code de l'application. Ne faites pas cela. Ils l'ont probablement placé juste pour montrer comment ils fonctionnent.
  • Garder votre code propre et maintenir un principe de responsabilité unique dépend de vous!

Exemple

Disons que vous avez une activité qui fait des choses bizarres et que le chargement du fragment prend beaucoup de temps, ce qui fait que vos tests Espresso échouent car vous ne pouvez pas trouver les ressources de votre fragment (vous devez changer la façon dont votre activité est créée et quand pour l'accélérer). Mais dans tous les cas, pour rester simple, l'exemple suivant montre à quoi cela doit ressembler.

Notre exemple de ressource d'inactivité obtiendrait deux objets:

  • La balise du fragment que vous devez trouver et en attente d'attacher à l'activité.
  • Un objet FragmentManager utilisé pour trouver le fragment.
/**
 * FragmentIdlingResource - idling resource which waits while Fragment has not been loaded.
 */
public class FragmentIdlingResource implements IdlingResource {
    private final FragmentManager mFragmentManager;
    private final String mTag;
    //resource callback you use when your activity transitions to idle
    private volatile ResourceCallback resourceCallback;

    public FragmentIdlingResource(FragmentManager fragmentManager, String tag) {
        mFragmentManager = fragmentManager;
        mTag = tag;
    }

    @Override
    public String getName() {
        return FragmentIdlingResource.class.getName() + ":" + mTag;
    }

    @Override
    public boolean isIdleNow() {
        //simple check, if your fragment is added, then your app has became idle
        boolean idle = (mFragmentManager.findFragmentByTag(mTag) != null);
        if (idle) {
            //IMPORTANT: make sure you call onTransitionToIdle
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }
}

Maintenant que vous avez votre IdlingResource écrite, vous devez l'utiliser quelque part correctement?

Usage

Sautons toute la configuration de la classe de test et examinons à quoi ressemblerait un scénario de test:

@Test
public void testSomeFragmentText() {
    mActivityTestRule.launchActivity(null);
   
    //creating the idling resource
    IdlingResource fragmentLoadedIdlingResource = new FragmentIdlingResource(mActivityTestRule.getActivity().getSupportFragmentManager(), SomeFragmentText.TAG);
    //registering the idling resource so espresso waits for it
    Espresso.registerIdlingResources(idlingResource1);
    onView(withId(R.id.txtHelloWorld)).check(matches(withText(helloWorldText)));

    //lets cleanup after ourselves
    Espresso.unregisterIdlingResources(fragmentLoadedIdlingResource);
}

Combinaison avec la règle JUnit

Ce n'est pas difficile; Vous pouvez également appliquer la ressource inactive sous la forme d'une règle de test JUnit. Par exemple, disons que vous avez un SDK contenant Volley et que vous voulez qu'Espresso l'attende. Au lieu de parcourir chaque scénario de test ou de l'appliquer dans la configuration, vous pouvez créer une règle JUnit et écrire simplement:

@Rule
public final SDKIdlingRule mSdkIdlingRule = new SDKIdlingRule(SDKInstanceHolder.getInstance());

Maintenant, puisque ceci est un exemple, ne le prenez pas pour acquis; tout le code ici est imaginaire et utilisé uniquement à des fins de démonstration:

public class SDKIdlingRule implements TestRule {
    //idling resource you wrote to check is volley idle or not
    private VolleyIdlingResource mVolleyIdlingResource;
    //request queue that you need from volley to give it to idling resource
    private RequestQueue mRequestQueue;

    //when using the rule extract the request queue from your SDK
    public SDKIdlingRule(SDKClass sdkClass) {
        mRequestQueue = getVolleyRequestQueue(sdkClass);
    }

    private RequestQueue getVolleyRequestQueue(SDKClass sdkClass) {
        return sdkClass.getVolleyRequestQueue();
    }

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //registering idling resource
                mVolleyIdlingResource = new VolleyIdlingResource(mRequestQueue);
                Espresso.registerIdlingResources(mVolleyIdlingResource);
                try {
                    base.evaluate();
                } finally {
                    if (mVolleyIdlingResource != null) {
                        //deregister the resource when test finishes
                        Espresso.unregisterIdlingResources(mVolleyIdlingResource);
                    }
                }
            }
        };
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow