BDD avec Cucumber

L’une des difficultés dans un processus de développement est de bâtir ce qui est  nécessaire, et juste ce qui est nécessaire. C’est l’un des objectifs de la méthodologie Agile, et dans la caisse à outils du développeur Agile, on trouve le Behavior Driven Development, qui permet  à l’équipe d’insuffler une dynamique de développement guidée par des tests d’intégration, eux-mêmes définies par les stories gérées par le Product Owner.
Pour le client, ce type d’outil permet de valider l’adéquation du code écrit avec le besoin exprimé, et aussi de formaliser ce besoin d’une manière qui marie le côté technique d’un développement logiciel au côté humain d’une expression de besoin.
Nous nous intéresserons ici au déploiement de l’outil.

Contexte

Imaginons que nous commencions un projet de développement mobile, que nous supposerons à réaliser sous iOS. Nous souhaiterions suivre une méthodologie Behavior Driven Development.
Un outil courant dans cette optique est Cucumber, qui permet de définir le logiciel à écrire sous la forme des comportements qu’il devra adopter (features et scenarii) dans un langage proche du langage naturel. Cet outil permet ensuite, à l’aide de fichiers steps écrits en Ruby par le QA, d’effectuer des tests d’intégration sur les fonctionnalités définies avec le Product Owner.

Préparation

Nous supposerons disposer d’un environnement de développement propre et à jour, avec, installés en global sur le système:
– Xcode
– Macports
– git via port
– rbenv -> on aura installé, en local au compte, Ruby 2.2.3 (rbenv global)
On dispose par ailleurs, aussi en local au compte:
– cocoa pods via gem
– nvm + npm + node 5.4.1

 

Calabash

On se crée ensuite un dossier qui contiendra notre projet, le mien se trouve dans ~/Documents/Projects/WeManity/Tests/CalabashExperimen…
On va ajouter un outil dérivé de Cucumber et spécifiquement approprié à iOS en passant par rbenv:
`gem install calabash-cucumber`
 La documentation de cet outil se trouve [ici]

 

 Le projet

On se crée maintenant un projet iOS dans le dossier que nous avons généré. On se contentera d’un projet de type “single view application”, et on choisira Objective-C.

 

  • On aurait pu utiliser Swift,  car calabash est liée sous la forme d’un framework. Swift étant en évolution permanente, autant rester sur une base stable pour mieux se concentrer sur notre objet !

 

On choisira d’intégrer Core Data et les tests unitaires, mais on s’abstiendra des tests UI (c’est l’objet principal de calabash).
On exécute maintenant la commande de génération d’un projet calabash, `calabash-ios gen`. Ceci nous propose de créer un dossier de travail et un fichier de gemmes (Gemfile), lequel sera initialisé avec la gemme `calabash-cucumber` utilisée.

 

  • On pourrait utiliser cocoapods pour installer Cucumber, mais l’équipe de Calabash déconseille son utilisation si le projet n’en fait pas encore usage. Comme nous nous concentrons sur calabash lui-même, c’est notre cas. En situation réelle, il suffira d’intégrer la dépendance calabash au Podfile.

 

Le dossier features

Le dossier features initialisé par calabash est un dossier cucumber classique: il contient une feature exemple (sample.feature), un dossier `steps` et un dossier `support` qui contient les scripts de cucumber.

Préparer Xcode

Nous allons créer une cible Xcode spécifique pour intégrer calabash. Il existe deux raisons majeures pour procéder ainsi. D’abord, cela permet de garder une séparation claire entre les tests d’intégration et l’application proprement dite, de la même façon qu’on a une cible pour les tests unitaires. En outre, Calabash est une dépendance corruptrice, car son mode de fonctionnement entraîne dans l’application compilée la présence de symboles et de méthodes sujets à rejet du logiciel par l’App Store.
Nous allons donc maintenir la version de test et la version de production séparées à l’aide de cibles différentes, munies d’identifiants différents.

 

Création de la cible

Duplication

On s’assure que le panneau des cibles est ouvert.

TargetPanel

On duplique alors la cible principale.

TargetDuplicate

Comme nous avons créé une application uniquement iPhone, Xcode souhaite gentiment nous aider à créer une application capable de gérer aussi l’iPhone. Pour simplifier notre propos, nous refusons cette proposition.

TargetDuplicateOnly

Maintenant, renommons notre cible “CalabashExperimentsIntegration” et renommons aussi les fichiers liés (Info.plist). On peut aussi choisir de rediriger le fichier Info.plist sur le même fichier que la version de production, mais on y perd l’avantage d’afficher un LaunchScreen spécifique pour rappeler qu’il s’agit de la version de tests d’intégration, ce qui peut se révéler utile quand on débogue des problèmes de certificats, de notifications push, par exemple. Dans mon dernier projet, la version “Production” fait appel à un serveur Google Cloud Platform, alors que la version de tests d’intégrations fait appel à un serveur Rails dédié explicitement au déboguage et au bouchonnage.

TargetRename

Création du schème

Dans la barre principale, on choisit le schème dupliqué et on sélectionne “gérer les schèmes”.

On renomme le schème comme on le souhaite (ici, CalabashExperimentsIntegration”). Il faut cocher la case “Partagé” (Shared), et la case “Montrer” (Show) est normalement pré-cochée.

On active alors le bouton “Edit”, sous-menu “Run”, et on confirme que l’exécutable actif est bien notre version de tests (ici, CalabashExperimentsIntegration), sans quoi le schème lancera la version de production, qui ne dispose pas du framework Calabash.

Frameworks

On doit ajouter à notre cible le framework CFNetwork.framework pour permettre à Calabash de lancer son serveur embarqué.

On dispose alors normalement du framework Apple approprié.

Nous pouvons dès lors nous attaquer à l’ajout du framework tierce partie calabash!

On commence par utiliser calabash-ios pour télécharger le framework avec `calabash-ios download`.

On ajoute le framework téléchargé comme on ajouterait un dossier classique.

Il nous reste à lier le framework dans la cible, en utilisant les paramètres suivants: `-ObjC -force_load “$(SOURCE_ROOT)/calabash.framework/calabash”`.

Tester notre cible en lançant une exécution sur le simulateur

Normalement, on doit assister à un lancement réussi de notre serveur calabash.

itworks

Lancement manuel

On suppose ici une batterie de tests lancée sur le simulateur, mais le processus est identique pour un test sur un appareil réel, aux chemins près.

 

  • Il est important que le simulateur soit déjà lancé, que le script tourne deux fois, ou que le timeout soit suffisamment long (au choix), sans quoi le lancement échouera si le simulateur ne démarre pas assez vite…

Exécutable de test

Dans le dossier du projet, on trouve un sous-dossier `features` créé à une étape précédente. Comme nous n’avons pas nommé notre cible de façon standard (avec une extension -cal) mais plutôt en utilisant “Integration” comme extension plus descriptive, il nous faut informer cucumber de la position correcte de l’exécutable.  Dans mon cas, il s’agit de `~/Library/Developer/Xcode/DerivedData/CalabashExperiments-gvolsunsuetoyhdovccegskmtgru/Build/Products/Debug-iphonesimulator/CalabashExperimentsIntegration.app`. Il suffit de définir la variable `APP_BUNDLE_PATH` dans le fichier features/support/01_launch.rb.

Connection d’un appareil

Certaines choses ne peuvent pas être testées en CI (par exemple, impossible de de tester un déplacement réel sur un appareil en fonctionnement tout en restant connecté dans des conditions de réseau déplorables, le réseau lui-même étant indispensable à la connection avec Calabash). Cependant, pour la majorité des cas, Apple recommande de tester sur un appareil. Voici comment connecter Calabash à notre iAppareil de test.

Conclusion

Nous avons désormais un Cucumber en état de fonctionnement, qui peut être

On peut alors lancer le test en ligne de commande avec `cucumber`. On peut bien entendu passer des features ou scenarii en particulier, afin d’automatiser les tests avec Jenkins, par exemple.


Cucumber, Jenkins, et Xcode Server forment une suite pour l’intégration continue qui permet de fournir une excellente vision en temps réel aux stakeholders du projet, de l’équipe de développement au commanditaire. Nous aborderons dans un document à venir le processus d’utilisation de Cucumber sur un exemple simplifié.