Fermer

novembre 6, 2019

React Native Test et automatisation de bout en bout avec Detox –


Detox est un environnement de test et d’automatisation de bout en bout fonctionnant sur un appareil ou un simulateur, tout comme un utilisateur final réel.

Le développement logiciel exige des réponses rapides aux besoins des utilisateurs et / ou du marché. Ce cycle de développement rapide peut entraîner (tôt ou tard) la rupture de certaines parties d'un projet, en particulier lorsque le projet prend de l'ampleur. Les développeurs sont submergés par toutes les complexités techniques du projet et même les hommes d’affaires commencent à avoir du mal à garder une trace de tous les scénarios envisagés par le produit.

Dans ce scénario, il est nécessaire que les logiciels restent au premier plan. le projet et nous permettent de déployer en toute confiance. Mais pourquoi des tests de bout en bout? Les tests unitaires et d’intégration ne sont-ils pas suffisants? Et pourquoi s'embarrasser de la complexité liée aux tests de bout en bout?

Tout d'abord, la plupart des cadres ont abordé le problème de la complexité, dans la mesure où certains outils (qu'ils soient gratuits, payants ou limités) nous permettent d’enregistrer le test en tant qu’utilisateur, puis de le rejouer et de générer le code nécessaire. Bien sûr, cela ne couvre pas l’ensemble des scénarios que vous pourrez traiter par programmation, mais c’est toujours une fonctionnalité très pratique.

Vous voulez apprendre React Native à partir de la base? Cet article est un extrait de notre bibliothèque Premium. Obtenez une collection complète de livres natifs de React couvrant les bases, les projets, les astuces, les outils, etc., avec SitePoint Premium. Inscrivez-vous maintenant pour seulement 9 dollars par mois .

Intégration de bout en bout et tests unitaires

Les tests de bout en bout par rapport aux tests d'intégration par rapport aux tests par unité: je trouve toujours le mot «versus». conduit les gens à prendre des camps – comme si c'était une guerre entre le bien et le mal. Cela nous pousse à prendre des camps au lieu d’apprendre les uns des autres et de comprendre le pourquoi plutôt que le comment. Les exemples sont innombrables: Angular versus React, React versus Angular versus Vue, et encore plus, React versus Angular versus Vue versus Svelte.

jQuery m'a rendu meilleur développeur en profitant du motif de façade $ ('') pour apprivoiser la bête sauvage du DOM et garder mon esprit concentré sur la tâche à accomplir. Angular a fait de moi un meilleur développeur en tirant parti de la composition des parties réutilisables en directives pouvant être composées (v1). React a fait de moi un meilleur développeur en tirant parti de la programmation fonctionnelle, de l’immutabilité, de la comparaison des références d’identité et du niveau de composabilité que je ne retrouve pas dans d’autres frameworks. Vue a fait de moi un meilleur développeur en tirant parti de la programmation réactive et du modèle push. Je pourrais continuer encore et encore, mais j'essaie simplement de démontrer que nous devons nous concentrer davantage sur le pourquoi: pourquoi cet outil a été créé en premier lieu, quels problèmes il résout et s'il existe d'autres moyens de le résoudre mêmes problèmes.

Plus vous montez, plus vous gagnez en confiance

graphique de test de bout en bout illustrant les avantages des tests de bout en bout et la confiance qu’ils procurent

Pour aller plus loin dans la simulation du parcours de l'utilisateur, vous devez effectuer davantage de travail pour simuler l'interaction de l'utilisateur avec le produit. Cependant, vous obtenez le maximum de confiance, car vous testez le produit réel avec lequel l'utilisateur interagit. Ainsi, vous interceptez tous les problèmes, qu'il s'agisse d'un problème de style susceptible de rendre invisible ou non interactif une section ou un processus d'interaction complet, un problème de contenu, un problème d'interface utilisateur, un problème d'API, un problème de serveur ou une base de données. problème. Tout cela est couvert, ce qui vous donne le plus de confiance.

Pourquoi Detox?

Nous avons discuté des avantages des tests de bout en bout et de leur utilité pour fournir le plus de confiance possible lors du déploiement de nouvelles fonctionnalités ou résoudre les problèmes. Mais pourquoi Detox en particulier? Au moment de la rédaction de cet article, il s’agissait de la bibliothèque la plus populaire pour les tests de bout en bout dans React Native et de la communauté la plus active. En plus de cela, c’est celui que React Native recommande dans sa documentation .

La philosophie du test Detox est le «test de la boîte grise». Les tests de boîte grise testent les cas où le framework connaît les composants internes du produit qu'il teste. En d'autres termes, il sait qu'il est dans React Native et sait comment démarrer l'application en tant qu'enfant du processus de Detox. et comment le recharger si nécessaire après chaque test. Chaque résultat de test est donc indépendant des autres.

Conditions préalables

  1. macOS High Sierra 10.13 ou supérieur
  2. Xcode 10.1 ou supérieur
  3. Homebrew:

      / usr / bin / ruby ​​-e "$ (curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
     
  4. Noeud 8.3.0 ou supérieur:

      noeud de mise à jour et à installer de la bière
     
  5. Apple Simulator Utilities: brassez-y le robinet et préparez-le à installer des pommes

  6. Detox CLI 10.0.7 ou plus:

      npm installer -g detox-cli
    

Voir le résultat à l'action

Tout d'abord, clonons un très intéressant projet open source React Native dans un souci d'apprentissage, puis ajoutons-y Detox:

 git clone https: / /github.com/ahmedam55/movie-swiper-detox-testing.git
cd film-swiper-detox-testing
npm installer
réactif natif run-ios

Créez un compte sur Le site Web Movie DB pour pouvoir tester tous les scénarios d'application. Ajoutez ensuite votre nom d'utilisateur et votre mot de passe dans le fichier .env avec respectivement nomutilisateurPlaceholder et passwordPlaceholder :

 isTesting = true
nom d'utilisateur = nom d'utilisateur
password = passwordPlaceholder

Après cela, vous pouvez maintenant exécuter les tests:

 test de désintoxication

Notez que je devais insérer un rapport dans le dépôt d'origine d'origine car il y avait beaucoup de changements radicaux entre detox-cli, detox et les bibliothèques du projet. Utilisez les étapes suivantes pour vous guider:

  1. Migration complète vers le dernier projet React Native.
  2. Mettez à jour toutes les bibliothèques pour résoudre les problèmes rencontrés par Detox lors des tests.
  3. Inverser les animations et les minuteries infinies si l'environnement est en cours de test.
  4. Ajouter le paquet de la suite de tests.

Configuration de nouveaux projets

Ajouter Detox à nos dépendances

Accédez au répertoire racine de votre projet et ajoutez Detox:

 npm install detox --save -dev

Configure Detox

Ouvrez le fichier package.json et ajoutez le texte suivant juste après le nom de projet config. Assurez-vous de remplacer movieSwiper dans la configuration iOS par le nom de votre application. Ici, nous disons à Detox où trouver l’application binaire et la commande pour la construire. (Ceci est facultatif. Nous pouvons toujours exécuter à la place.) Choisissez également le type de simulateur: ios.simulator ios.none . , android.emulator ou android.attached . Et choisissez l’appareil à tester:

 {
  "name": "movie-swiper-detox-testing",

  // ajoute ces derniers:
  "detox": {
    "configurations": {
      "ios.sim.debug": {
        "chemin binaire": "ios / build / movieSwiper / Build / Produits / Debug-iphonesimulator / movieSwiper.app",
        "build": "xcodebuild -project ios / movieSwiper.xcodeproj -scheme movieSwiper -configuration Debug -sdk iphonesimulator -derivedDataPath ios / build",
        "type": "ios.simulator",
        "name": "iPhone 7 Plus"
      }
    }
  }
}

Voici la composition de la configuration ci-dessus:

  • Exécutez pour exécuter l'application native binaire pour créer l'application binaire.
  • Recherchez l'application binaire située à la racine du projet: trouver. -name "* .app" .
  • Mettez le résultat dans le répertoire build .

Avant de lancer la suite de tests, assurez-vous que le périphérique est nommé . spécifié est disponible (par exemple, iPhone 7). Vous pouvez le faire depuis le terminal en exécutant ce qui suit:

 xcrun simctl list

Voici à quoi cela ressemble:

 device-list

Maintenant que nous avons ajouté Detox à notre projet et lui avons indiqué le simulateur avec lequel démarrer l'application, nous avons besoin d'un testeur pour le gérer. les assertions et les rapports - qu'ils soient sur le terminal ou autrement.

Detox prend en charge Jest et Mocha . Nous irons avec Jest, car il a une plus grande communauté et un plus grand ensemble de fonctionnalités. En plus de cela, il prend en charge l'exécution de tests parallèles, ce qui peut être pratique pour accélérer les tests de bout en bout à mesure que leur nombre augmente.

Ajout de Jest aux dépendances de développement

Exécutez les opérations suivantes pour installer Jest: 19659027] npm installer jest jest-cli --save-dev

Générer des fichiers de suite de tests

Pour initialiser Detox à utiliser Jest, exécutez la procédure suivante:

 detox init -r jest

Ceci créera un dossier e2e à la racine du projet et à l'intérieur de celui-ci:

  • e2e / config.json contient la configuration globale du testeur:

     {
          "setupFilesAfterEnv": ["./init.js"],
          "testEnvironment": "noeud",
          "journalistes": ["detox/runners/jest/streamlineReporter"],
          "verbose": true
      }
     
  • e2e / init.js contient le code d'initialisation exécuté avant l'exécution de l'un de vos tests:

     const detox = require ('detox');
      const config = require ('../ package.json'). detox;
      const adapter = require ('detox / runners / jest / adapter');
      const specReporter = require ('detox / runners / jest / specReporter');
    
      // Définir le délai d'attente par défaut
      jest.setTimeout (25000);
      jasmine.getEnv (). addReporter (adaptateur);
    
      // Cela permet de générer des journaux d'état sur une base spécifique. Par défaut, jest ne rapporte que des fichiers.
      // Ceci est strictement optionnel.
      jasmine.getEnv (). addReporter (specReporter);
    
      beforeAll (async () => {
        wait detox.init (config);
      });
    
      beforeEach (async () => {
        attendez adapter.beforeEach ();
      });
    
      afterAll (async () => {
        wait adapter.afterAll ();
        attendez detox.cleanup ();
      });
     
  • e2e / firstTest.spec.js est le fichier test de Detox par défaut. C'est là que nous allons mettre tous les tests pour l'application. Nous parlerons en détail des blocs des blocs et ainsi que des suites de tests que nous allons créer ultérieurement.

Enfin, nous exécutons les tests [19659039] Pour exécuter les tests, accédez au répertoire racine de votre projet et exécutez les opérations suivantes:

 test de désintoxication

Félicitations! Tout est prêt pour que nous puissions rédiger nos tests impressionnants Vous pouvez créer et gérer autant de fichiers e2e / * spec.js que vous le souhaitez et le testeur les exécutera un par un. Le fichier de spécification représente un ensemble indépendant de fonctionnalités que vous souhaitez tester. Par exemple, extraire, extraire, authentifier l'utilisateur ou s'inscrire.

Dans le fichier de spécifications que vous avez décrivez . Il contient les plus petits blocs de test, le bloc le bloc – créé pour la lecture. Par exemple: il devrait refuser de créer un compte si le nom existe déjà . Et à l'intérieur de ce bloc il vous ajoutez les assertions nécessaires pour vous assurer que cela est vrai. Idéalement, nous devrions recharger React Native après chaque bloc le . C’est tant qu’ils ne dépendent pas les uns des autres. Cela évite les faux positifs et facilite le débogage. Sachant que ce test a échoué, vous n'avez pas à vous soucier de tous les autres scénarios.

Une plongée en profondeur dans notre suite de tests

Nous vérifierons si l'application répond aux scénarios suivants.

  • Il devrait interdire la connexion avec de fausses informations d'identification . Celle-ci semble évidente, mais elle est essentielle au flux de travail de l'application. Elle doit donc être testée à chaque modification et / ou déploiement.
  • Elle doit authentifier les utilisateurs avec des informations d'identification valides – pour tester le fonctionnement de la fonctionnalité d'authentification.
  • Il devrait expulser les utilisateurs lorsqu'ils se déconnectent – en vérifiant si la déconnexion permet aux utilisateurs de s'éloigner des écrans Parcourir, Explorer et Bibliothèque.
  • Il devrait permettre aux invités de parcourir écran seulement . Les utilisateurs peuvent se connecter ou continuer en tant qu'invités et, dans ce cas, ils ne pourront accéder qu'à l'écran Parcourir et à ses fonctions.
  • Les films correspondant à la requête doivent être recherchés. Les films rendus sont ceux qui correspondent à la requête de recherche.
  • Il convient de l'ajouter aux favoris – pour tester la fonctionnalité d'ajout de films préférés et pour vous assurer que le film ajouté apparaît dans la liste des films favoris. [19659017] Il devrait s’ajouter à la liste de surveillance – comme pour tester l’ajout de films à vos films favoris, mais pour la fonctionnalité de liste de surveillance.
  • Il devrait s'afficher lorsque l'on clique davantage plus fonctionnalité des boutons de la section Parcourir:
    • Trending Daily
    • Trending Weekly
    • Populaires
    • Top Rated
    • Assurez-vous qu'il navigue vers la liste des films avec tous les films correspondant aux critères sélectionnés.

Parcourir le code du Test Suite

Il est temps de passer en revue le code permettant de tester l'application. Avant de le faire, cependant, je vous recommande de lancer d'abord l'application sur votre appareil ou votre simulateur. C’est pour vous familiariser avec les différents écrans et composants de l’interface utilisateur de l’application.

La première chose à faire est de définir les fonctions que nous utiliserons pour effectuer divers tests. Tandis que je me trouvais en train de faire correspondre le même ensemble d’éléments d’interface utilisateur et d’effectuer un ensemble d’actions spécifique, je l’attribuais à sa propre fonction pour pouvoir le réutiliser dans d’autres tests et centraliser les correctifs et les modifications au même endroit. Voici quelques exemples d'abstraction que j'ai trouvés utiles:

  • loginWithWrongCredentials ()
  • loginWithRightCredentials ()
  • goToLibrary () signOut ()
  • searchForMovie (title)

L'API de Detox devrait facilement avoir un sens, même si vous ne l'avez pas utilisé auparavant. Voici le code:

 // e2e / firstTestSuite.spec.js

// récupère le nom d'utilisateur et le mot de passe du fichier .env
const username = process.env.username;
const password = process.env.password;

const sommeil = durée =>
  nouvelle promesse (resolution => setTimeout (() => resolve (), durée)); // fonction pour suspendre l'exécution du test. Principalement utilisé pour attendre qu'un composant spécifique de l'interface utilisateur apparaisse à l'écran

const loginWith = async (nom d'utilisateur, mot de passe) => {
  essayer {
    // cliquez sur login btn pour accéder au nom d'utilisateur, à l'écran de mot de passe
    const browseToLoginBtn = wait element (by.id ("browse-login-btn"));
    attend Naviguer dans ToLoginBtn.tap ();

    const usernameInput = wait element (by.id ("username-input"));
    const passwordInput = wait element (by.id ("password-input"));

    wait usernameInput.tap ();
    wait usernameInput.typeText (nom d'utilisateur);
    wait passwordInput.typeText (mot de passe);

    const loginBtn = wait element (by.id ("login-btn"));

    attendez loginBtn.tap (); // pour fermer le clavier
    attendez loginBtn.tap (); // pour démarrer le processus d'authentification

    const errorMessage = wait element (
      by.text ("Nom d'utilisateur et / ou mot de passe invalide")
    )

    return {errorMessage, usernameInput, passwordInput};
  } catch (e) {
    console.log (
      "Une déconnexion n'a pas été faite, ce qui a rendu le fichier` browse-login-btn` introuvable "
    )
  }
};

const loginWithWrongCredentials = async () =>
  attendez loginWith ("alex339", "9sdfhsakjf"); // se connecter avec des identifiants aléatoires incorrects
const loginWithRightCredentials = async () =>
  attendez loginWith (nom d'utilisateur, mot de passe); // se connecter avec les informations d'identification correctes

const goToLibrary = async () => {
  const libraryBtn = wait element (by.id ("navigation-btn-Library"));
  wait libraryBtn.tap ();
};

const goToExplore = async () => {
  const exploreBtn = wait element (by.id ("navigation-btn-Explore"));
  attendez exploreBtn.tap ();
};

const signOut = async () => {
  attendez goToLibrary ();

  const settingsBtn = wait element (by.id ("settings-btn"));
  attendez settingsBtn.tap ();

  const signOutBtn = wait element (by.id ("log-out-btn"));
  attendez signOutBtn.tap ();
};

const continueAsGuest = async () => {
  const continueAsGuestBtn = wait element (by.id ("continue-as-guest"));
  attendez continueAsGuestBtn.tap ();
};

const searchForMovie = async movieTitle => {
  const searchMoviesInput = wait element (by.id ("search-input-input"));
  attend searchMoviesInput.tap ();
  wait searchMoviesInput.clearText ();
  wait searchMoviesInput.typeText (movieTitle);
};

const goBack = async () => {
  const goBackBtn = wait element (by.id ("go-back-btn"));
  goBackBtn.tap ();
};

const goToWatchListMovies = async () => {
  const watchListBtn = wait element (by.id ("my-watchlist"));
  attendez watchListBtn.tap ();
};

const goToFavoriteMovies = async () => {
  const favourMoviesBtn = wait element (by.id ("mes films préférés"));
  attendez favoriteMoviesBtn.tap ();
};

const clickFavoriteButton = async () => {
  const addToWatchListBtn = wait element (by.id ("add-to-favorite-btn"));
  attendez addToWatchListBtn.tap ();
};

const clickWatchListButton = async () => {
  const addToWatchListBtn = wait element (by.id ("add-to-watch-list-btn"));
  attendez addToWatchListBtn.tap ();
};

const removeTestMoviesFromLists = async () => {
  essayer {
    attendez loginWithRightCredentials ();
    attendez goToLibrary ();
    attendez goToWatchListMovies ();

    const movieItemInWatchList = wait élément (
      by.text ("Crazy Rich Asians"). withAncestor (by.id ("liste de surveillance"))
    )

    wait movieItemInWatchList.tap ();
    attend clickWatchListButton ();
    attendez goToLibrary ();
    attendez goToFavoriteMovies ();

    const movieItemInFavorites = attendre l'élément (
      by.text ("Avengers: Endgame"). withAncestor (by.id ("favorite-list"))
    )

    wait movieItemInFavorites.tap ();
    attendez clickFavoriteButton ();
  } catch (e) {}
  wait signOut ();
};

// next: add fonction pour affirmer des éléments de film

Ensuite, nous ajoutons la fonction permettant d'affirmer les éléments du film. Contrairement à toutes les autres fonctions que nous avons définies ci-dessus, celle-ci exécute un test individuel pour affirmer qu'un élément de film spécifique est visible à l'écran:

 const assertMovieItems = async (moviesTitles = []) => {
  for (let i = 0; i <moviesTitles.length; i ++) {
    const moviesItem = wait element (by.text (moviesTitles [i]));
    wait expect (moviesItem) .toBeVisible ();
  }
};

// next: crée la suite de tests

À ce stade, nous sommes prêts à créer la suite de tests. Cela devrait être enveloppé dans un bloc décris . Pour que chaque test ait un point de départ «propre», nous utilisons les méthodes de cycle de vie suivantes:

  • beforeAll : exécuté une fois avant l'exécution de cette suite de tests. Dans ce cas, nous appelons la fonction removeTestMoviesFromLists () . Comme vous l'avez vu précédemment, ceci est l'équivalent d'une séquence de vérification au démarrage dans laquelle l'utilisateur se connecte et visite différentes pages et clique sur les différents boutons qui seront utilisés lors des tests. Cela garantit que l'application est dans un état fonctionnel minimal avant de commencer à exécuter les tests.
  • beforeEach : exécuté avant chaque test de cette suite de tests. Dans ce cas, nous voulons recharger React Native. Notez que cela a le même effet que si vous appuyez sur + r rr ou Ctrl + r dans votre clavier.
  • afterEach : exécuté après chaque test de cette suite de tests. Dans ce cas, nous voulons déconnecter l'utilisateur, ce qui signifie que dans chacun de nos tests, nous devons le reconnecter. Encore une fois, il est recommandé de commencer à écrire des tests: chaque test doit avoir la même point de départ. Cela garantit qu'ils peuvent fonctionner dans n'importe quel ordre tout en produisant les mêmes résultats:
     décrit ("Project Test Suite", () => {
        beforeAll (async () => {
          attendez removeTestMoviesFromLists ();
        });
    
        beforeEach (async () => {
          wait device.reloadReactNative ();
        });
    
        afterEach (async () => {
          essayer {
            wait signOut ();
          } catch (e) {}
        });
    
        // next: lance les tests individuels
      });
       

Passons maintenant aux tests individuels. Ceux-ci peuvent être définis à l'intérieur d'un bloc il . Chaque bloc il part de zéro et énonce un scénario spécifique et bien défini (ceux que nous avons décrits dans la section précédente). Chaque test a une sortie prévisible, c’est ce que nous devons affirmer:

 it ("devrait interdire la connexion avec de mauvaises références", async () => {
  const {
    Message d'erreur,
    nom d'utilisateurInput,
    passwordInput
  } = attend loginWithWrongCredentials ();

  wait expect (errorMessage) .toBeVisible ();
  wait expect (usernameInput) .toBeVisible ();
  wait expect (passwordInput) .toBeVisible ();
});

it ("devrait se connecter avec les bonnes références", async () => {
  attendez loginWithRightCredentials ();

  attendez goToLibrary ();

  const watchListBtn = element (by.id ("ma-liste de suivi"));
  const favourMoviesBtn = element (by.id ("mes films préférés"));

  wait expect (watchListBtn) .toBeVisible ();
  wait expect (favoriteMoviesBtn) .toBeVisible ();
});

it ("devrait expulser l'utilisateur lorsque l'utilisateur clique sur la déconnexion", async () => {
  attendez loginWithRightCredentials ();
  attendez goToLibrary ();
  wait signOut ();

  const loginBtn = wait element (by.id ("browse-login-btn"));
  wait expect (loginBtn) .toBeVisible ();
});

it ("devrait autoriser l'invité à parcourir uniquement", async () => {
  attendez continueAsGuest ();
  attendez goToLibrary ();

  const watchListBtn = element (by.id ("ma-liste de suivi"));
  const favourMoviesBtn = element (by.id ("mes films préférés"));

  wait expect (watchListBtn) .toBeNotVisible ();
  wait expect (favoriteMoviesBtn) .toBeNotVisible ();

  attendez goToExplore ();

  const moviesSwipingView = element (by.id ("movies-swiping-view"));

  wait expect (moviesSwipingView) .toBeNotVisible ();
});

it ("devrait chercher et rendre les recherches correctement", async () => {
  attendez loginWithRightCredentials ();

  recherches const = [
    {
      query: "xmen",
      results: ["X-Men: Apocalypse", "X-Men: Days of Future Past"]
    },
    {
      requête: "vengeurs",
      résultats: ["Avengers: Endgame", "Avengers: Age of Ultron"]
    },
    {requête: "carcajou", résultats: ["Logan", "The Wolverine"]}
  ];

  pour (soit i = 0; i < searches.length; i++) {
    const currentSearch = searches[i];

    await searchForMovie(currentSearch.query);
    await assertMovieItems(currentSearch.results);
  }
});

it("should add to favorite", async () => {
  attendez loginWithRightCredentials ();

  wait searchForMovie ("avengers");
  wait element (by.text ("Avengers: Endgame")). tap ();

  attendez clickFavoriteButton ();
  attendez goBack ();
  attendez goToLibrary ();
  attendez goToFavoriteMovies ();

  attendre le sommeil (3000);

  var movieItemInFavorites = attend l'élément (
    by.id ("favorite-list"). withDescendant (by.text ("Avengers: Endgame"))
  )

  wait expect (movieItemInFavorites) .toBeVisible ();
});

it ("devrait ajouter à la liste de suivi", async () => {
  attendez loginWithRightCredentials ();

  attend searchForMovie ("fou fou");
  wait element (by.text ("Crazy Rich Asians")). tap ();

  attend clickWatchListButton ();

  attendez goBack ();
  attendez goToLibrary ();
  attendez goToWatchListMovies ();

  attendre le sommeil (3000);

  const movieItemInFavorites = attendre l'élément (
    by.id ("liste de surveillance"). withDescendant (by.text ("Crazy Rich Asians"))
  )

  wait expect (movieItemInFavorites) .toBeVisible ();
});

it ("devrait montrer que toutes les listes sont cliquées", async () => {
  attendez loginWithRightCredentials ();

  const trendingDailyMoreBtn = wait element (by.id ("trending-daily-more"));
  attendez trendingDailyMoreBtn.tap ();

  attendez goBack ();
  attendre le sommeil (300);

  const trendingWeeklyMoreBtn = wait element (by.id ("trending-hebdomadaire-plus"));
  wait trendingWeeklyMoreBtn.tap ();

  attendez goBack ();
  attendre le sommeil (300);

  const popularMoreBtn = wait element (by.id ("popular-more"));
  attendez popularMoreBtn.tap ();

  attendez goBack ();
  attendre le sommeil (300);

  const browseSectionsView = wait element (by.id ("browse-sections-view"));
  wait browseSectionsView.scrollTo ("bottom");

  const topRatedMoreBtn = wait element (by.id ("top-rated-more"));
  wait topRatedMoreBtn.tap ();
});

Dans le code ci-dessus, vous pouvez voir que le flux de travail de chaque test peut être résumé en quatre étapes:

  1. Initialisez l'état . C'est ici que nous connectons l'utilisateur pour que chaque test ait le même point de départ.
  2. Sélectionnez le composant d'interface utilisateur . C'est là que nous utilisons des adaptateurs pour cibler des composants spécifiques de l'interface utilisateur.
  3. Déclenchez l'action . C’est là que nous déclenchons l’action sur le composant d’interface utilisateur que nous avons sélectionné.
  4. Affirmons que le résultat attendu existe ou n’existe pas . C'est là que nous utilisons la méthode expect () pour vérifier si l'action a déclenché l'affichage ou le masquage d'un autre composant de l'interface utilisateur. Si l'assertion est renvoyée true le test est réussi.

Remarque: en raison de la nature en constante évolution de l'application, les éléments de film que nous affirmons peuvent changer très souvent. Si vous lisez ceci quelque temps après la publication de cette pièce, assurez-vous d’abord de vérifier manuellement si des éléments spécifiques sont visibles à l’écran. Cela permet d'éviter que le test échoue inutilement et vous évite d'avoir à faire fonctionner la démo.

Matchers

Vous pouvez associer ou sélectionner n'importe quel élément de l'interface utilisateur par ID, texte, libellé, parent, enfant (à n'importe quel niveau). , ou traits. Voici quelques exemples:

 const usernameInput = wait element (by.id ("username-input"));
 
 const errorMessage = wait element (by.text ("Nom d'utilisateur et / ou mot de passe invalide"));

Actions à exécuter

Detox peut effectuer un grand nombre d'actions sur des éléments de l'interface utilisateur: tap longPress multiTap tapAtPoint swipe typeText clearText faites défiler faites défiler vers et autres .

En voici quelques exemples:

 wait usernameInput.tap ();

wait usernameInput.typeText (nom d'utilisateur);

wait passwordInput.clearText ();

const browseSectionsView = wait element (by.id ("browse-sections-view"));

wait browseSectionsView.scrollTo ("bottom");

Assertions à tester

Detox dispose d'un ensemble d'assertions pouvant être exécutées contre des éléments de l'interface utilisateur correspondants: à BeVisible àNotBeVisible à [Non]. àNotExist àHaveText àHaveLabel àHaveTd àHaveValue . Voici quelques exemples:

 const assertMovieItems = async (moviesTitles = []) => {
  for (let i = 0; i <moviesTitles.length; i ++) {
    const moviesItem = wait element (by.text (moviesTitles [i]));
    wait expect (moviesItem) .toBeVisible ();
  }
};

wait assertMovieItems (["Avengers: Endgame", "Avengers: Age of Ultron"]);
 
 const watchListBtn = element (by.id ("ma-liste de suivi"));
wait expect (watchListBtn) .toBeNotVisible ();

Défis et recettes

Animations en boucle sans fin ou minuteries

L'un des problèmes auxquels j'ai été confronté est que Detox s'arrête s'il existe une boucle de minuterie ou une animation qui ne se termine jamais. J'ai dû procéder comme suit pour résoudre ces problèmes:

  1. Rechercher et déboguer des pièces dans l'arborescence des applications et les importer en les modifiant et les supprimant.
  2. Exécutez à nouveau la suite de tests pour vérifier si le problème persiste.
  3. Après cela et la plupart du temps, le problème est une animation qui commence tout de suite après son achèvement. J'ai donc importé de réact-native-config un outil très pratique pour définir des variables d'environnement permettant de basculer certains comportements ou fonctionnalités en fonction de l'environnement. Dans mon cas, il ajoutait à isTesting = true dans le fichier .env en vérifiant sa présence dans la base de code et en désactivant la boucle d’animation ou en diminuant considérablement la durée, de façon à accélérer la suite de tests.

Comme vous pouvez le constater, il s’agit essentiellement de jouer avec les paramètres d’animation de votre application. Pour plus d'informations sur le dépannage de Detox, vous pouvez consulter la documentation suivante:

Ajout de TestID à l'élément d'interface utilisateur appropriée

Un autre défi consiste à creuser le composant pour passer le testID à, comme Detox ne le fait pas. ne le supporte pas pour les composants personnalisés. Il est parfois nécessaire d’envelopper le composant avec un composant intégré, tel que le composant View afin de le faire correspondre et d’interagir avec lui. Cela est particulièrement vrai si le code du composant intégré interne est une bibliothèque importée dans le dossier node_modules .

Compose TestID avec des données contextuelles

Un autre scénario que j'ai dû gérer est celui des composants qui sont: rendu dans plusieurs endroits avec différents gestionnaires d'événements et titres. Je devais donc créer un testID composite portant le titre, les majuscules et les tirets, ainsi que l'identificateur testID du composant.

Par exemple, plus bouton de toutes les sections de navigation: il s'agit du même composant rendu pour chacune d’elles:

 const testID = `$ {(this.props.title || ''). ToLowerCase (). Replace (/  s / g, '-')} - more`

 revenir (
  ...
    
       PLUS
    
 }

Parfois, ce n'est pas un accessoire, mais plutôt des enfants, donc vous finissez par les filtrer et les mapper pour obtenir le nœud de texte et sa valeur.

Narrowing Down Selectors

Comme certains navigateurs ont tendance à garder Dans les écrans précédents de l’arborescence, Detox trouverait deux éléments portant le même identifiant (texte, identifiant, libellé) et lèverait une exception. Ainsi, nous devons filtrer les éléments d'un écran spécifique pour obtenir ce dont nous avons besoin. Vous pouvez le faire en utilisant l'élément withAncestor () qui correspond à un ancêtre spécifique:

 const movieItemInWatchList = wait element (
  by.text ("Crazy Rich Asians"). withAncestor (by.id ("liste de surveillance"))
)

wait movieItemInWatchList.tap ();

Voyons le résultat d’une manière plus engageante

Vous pouvez consulter un enregistrement des tests en cours ci-dessous. Vous devriez obtenir des résultats similaires lorsque vous exécutez les tests pour l'application.

Pour simuler la saisie de texte, le clavier doit apparaître lorsqu'une entrée est sélectionnée. Pour l'activer, accédez à Simulator> Keyboard> Toggle Software Keyboard . Effectuez cette étape avant de commencer à exécuter les tests.

Conclusion

Dans ce didacticiel, vous avez appris à utiliser Detox pour mettre en œuvre des tests de bout en bout dans votre application React Native.

pour ajouter la configuration Detox permettant d'exécuter les tests sur iOS, écrivez des sélecteurs pour interagir avec les composants de l'interface utilisateur et assurez-vous qu'un contenu spécifique existe à l'écran après une interaction avec l'interface utilisateur. Enfin, vous avez appris certains des problèmes les plus courants que vous pourriez rencontrer et comment les résoudre.

Nous n’avons testé iOS que dans ce didacticiel, mais vous devriez également pouvoir exécuter les tests sur Android. Notez que vous devrez peut-être rétrograder votre application vers une version inférieure de React Native et Detox pour que celle-ci fonctionne sur Android. C'est parce que la prise en charge iOS est meilleure en Detox.

Vous pouvez afficher le code source sur ce rapport GitHub .




Source link