Partie 3 : Améliorer notre code pour en faire un test réutilisable

Nous avons appris dans la partie 2 que nous pouvions contrôler de manière simple Firefox OS en utilisant les commandes du client Marionette. Néanmoins, les saisir dans une console Python est aussi lent que fastidieux. L'avantage clé de l'automatisation de tests est son exécution autonome. Nous allons apprendre comment faire cela dans cette partie, en mettant toutes nos commandes dans un fichier Python qui pourra s'exécuter d'un seul coup.

Résumé d'un cas de test

Dans la partie 2, nous avons passé les étapes pour exécuter un cas de test typique — ouvrir l'application Contacts et ajouter un nouveau contact :

  1. Déverrouiller Firefox OS (facultatif ; dans la partie 2 nous avons désactivé l'écran de verrouillage manuellement, en conséquence nous ne l'incluerons pas dans le code ci-dessous)
  2. Aller dans l'application Contacts
  3. Appuyer sur l'icône d'ajout d'un nouveau contact
  4. Saisir le nom du contact
  5. Appuyer sur OK
  6. Attendre et vérifier que le contact est présent

Mettre notre test dans un fichier Python

Si nous mettons toutes ces étapes dans un fichier Python, nous pouvons le réutiliser et l'exécuter bien plus rapidement. Créez un nouveau fichier texte nommé test_add_contact.py dans un dossier convenable de votre choix.

Dans ce fichier, entrez les commandes que nous avons vues dans la partie 2, comme listé ci-dessous. Nous utiliserons une structure de classes Python, puisque c'est une bonne pratique et qui plus est elle constitue une bonne base pour construire les étapes à venir de ce tutoriel.

import time
from marionette import Marionette

class TestContacts:

    def __init__(self):
        self.test_add_contacts()

    def test_add_contacts(self):
        # Create the client for this session. Assuming you're using the default port on a Marionette instance running locally
        self.marionette = Marionette()
        self.marionette.start_session()

        # Switch context to the homescreen iframe and tap on the contacts icon
        time.sleep(2)
        home_frame = self.marionette.find_element('css selector', 'div.homescreen iframe')
        self.marionette.switch_to_frame(home_frame)
        contacts_icon = self.marionette.find_element('xpath', "//div[@class='icon']//span[contains(text(),'Contacts')]")
        contacts_icon.tap()

        # Switch context back to the base frame
        self.marionette.switch_to_frame()
        time.sleep(2)

        # Switch context to the contacts app
        contacts_frame = self.marionette.find_element('css selector', "iframe[data-url*='contacts']")
        self.marionette.switch_to_frame(contacts_frame)

        # Tap [+] to add a new Contact
        self.marionette.find_element('id', 'add-contact-button').tap()
        time.sleep(2)

        # Type name into the fields
        self.marionette.find_element('id', 'givenName').send_keys('John')
        self.marionette.find_element('id', 'familyName').send_keys('Doe')

        # Tap done
        self.marionette.find_element('id', 'save-button').tap()
        time.sleep(2)

        # Close the Marionette session now that the test is finished
        self.marionette.delete_session()

if __name__ == '__main__':
    TestContacts()

Note : il y a une chose que vous allez remarquer dans le code que nous n'avons pas couverte dans la partie 2 : la fonction Python time.sleep() — cela met le script en pause pour une certaine durée (définie en secondes) avant qu'il ne continue jusqu'à la ligne suivante. Nous avons ajouté ces lignes dans le test automatisé car nous devons simuler l'utilisateur qui, manuellement, appuie sur les boutons, etc. et attendre que Firefox OS effectue les actions qui en résultent. Si nous exécutons le script sans pause, Python effectuera toutes les actions de manière instantanée, mettant probablement le test en échec, puisque Firefox OS n'aurait pas été capable de s'adapter.

À présent, nous pouvons lancer le test en allant dans le fichier où le test est enregistré via le terminal et en exécutant la commande suivante :

python test_add_contact.py

Note : soyez vigilant par rapport aux règles d'identation de Python. Après avoir copié et collé le code vous devrez probablement tout identer correctement pour qu'il s'exécute. Si vous obtenez une erreur liée à ce point, assurez-vous que tous les niveaux d'indentation sont séparés par une tabulation.

Note : vous remarquerez également que le nom inséré dans le code ci-dessus est "John Doe", à la différence du nom "Foo Bar" de la partie 2. Nous avons fait ce changement dans le code pour qu'il puisse ajouter avec succès un autre contact. Si vous essayez d'ajouter un contact avec le même nom, Firefox OS vous alertera sur le doublon de contacts. Pour l'instant, la meilleure façon de répéter l'exécution du test est d'aller dans l'interface de Firefox OS et de supprimer manuellement le contact avant chaque exécution.

Ajouter une assertion

L'élément qui manque toujours dans notre test, qui est important dans les tests automatisés, est une assertion — un rapport ou mesure pour déterminer si Firefox OS a atteint l'état que nous voulions qu'il atteigne ou si le test est un succès. Nous allons régler ce problème en ajoutant du code pour vérifier si le nouveau contact est présent dans l'application.
 
Juste avant la ligne # Close the Marionette session… ajoutez dans ce code, en s'assurant qu'il est indenté au même niveau que les autres lignes de la classe :

# Now let's find the contact item and get its text
contact_name = self.marionette.find_element('css selector', 'li.contact-item:not([data-group$="ice"]) p').text     
assert contact_name == 'John Doe'

Supprimez l'ancien contact et essayer d'exécuter de nouveau le test, avec la commande suivante :

python test_add_contact.py

Si cela s'exécute correctement, alors nous avons un test qui fonctionne !

Note : si l'assertion échoue, assurez-vous que le précédent contact 'Foo Bar' n'existe plus. Le sélecteur CSS avant l'assertion prend en compte le premier contact de la liste (cela pourrait être visible en appelant print "Contact name: %s" % contact_name avant l'appel de l'assertion).

Note : l'assertion n'apparaîtra pas pour faire quelque chose, mais les assertions sont très importantes quand nous commençons à utiliser des exécuteurs de tests, comme présenté dans la Partie 5: Présentation d'un exécuteur de tests. Les exécuteurs de test comme unittest utilisent les assertions pour vérifier si les tests ont été réalisés avec succès ou non, et ensuite retournent les résultats de ces tests (OK ou FAIL.)

Note sur le timing

Une des choses les plus difficiles à gérer en écrivant un test automatisé est le timing. Si le test passe à l'étape suivant avant que Firefox OS n'effectue la précédente, alors nous allons probablement avoir un échec du test.
 
Comme mentionné ci-dessus, dans l'exemple de code que nous avons ajouté, la commande time.sleep(x) résout ce problème. Pourtant, utiliser time.sleep(x) n'est pas une bonne pratique. Utiliser une temporisation codée en dur peut faire que le test va s'exécuter trop longtemps ou pas assez. Le dernier cas est le pire ; il donnera des résultats de tests faux négatifs — ce qui signifie un test qui remonte un échec quand en réalité l'application fonctionne parfaitement mais se comporte de manière plus lente que ce que le test espérait.

Dans la partie suivante, nous aborderons l'abstraction de certains points du test vers des fonctions Pythons distinctes,  et remplacerons les fonctions sleep() par les temporisations dynamiques appropriées.

Étiquettes et contributeurs liés au document

 Contributeurs à cette page : jwhitlock, SphinxKnight, Goofy, noahchampoux, gmealer, J.DMB
 Dernière mise à jour par : jwhitlock,