Tests fonctionnels

Selenium, Behat et CasperJS
Mais pas que ça






 Twitter: @Rebolon
 GitHub: http://github.com/Rebolon

En PHP


on peut tester son code et son application


Oui c'est possible,

[Et ça devrait même être obligatoire]

Les tests


  • Niveau 0 : Tester manuellement
  • Niveau 1 : Tests unitaires
  • Niveau 2 : Tests fonctionnels

Tests manuels


It's Bad
Really Bad
Mais c'est mieux que rien...

Tests Unitaires


  • Tester le fonctionnement d'une méthode
  • Vérifier que la gestion des erreurs fonctionnent (ex: passage de mauvaises entrées)
  • Tester les différentes rêgles de gestion gérées par la méthode

Problèmes classiques

  • C'est mal d'être dépendant  
    new MaClasseKiDechireMaisKiPlanteMonTest();
    Solution : l'Injection de dépendance
  • Attention bis ! la cohérence des données provenant de la base.
    Mockez vous de vos classes (projet Mockery notamment)

Et surtout

  • It's Alive ! oui votre code vie, vos tests doivent vivre en même temps
Avantages : vous validez le fonctionnement de vos méthodes et le respect de la spécification détaillée (la quoi ?)

Inconvénients : ce n'est pas suffisant car on ne garantie pas le bon fonctionnement de la chaîne applicative 

Tests fonctionnels


Objectif simple : tester une fonctionnalité dans son ensemble

Comment fait-on ?

On croise les doigts ?
  • Plugins navigateurs : Selenium
  • Les Emulateurs : Behat, ZombieJS...
  • Les HeadLess Browser : PhantomJS, Selenium Server (+Web Driver)

Plugin Navigateur


Permet d'enregistrer ses scénarios via un plugin FF / GC et naviguant sur un site et en définissant certains tests (assertTitle, assertSlectorExists...)

Selenium peut automatiser la lecture de ces scénarios via Selenium Server with WebDriver (ex Remote Control)

Browser Emulator


  • Basé sur les spécifications HTTP  et DOM
  • Permet de faire des requêtes HTTP
  • Permet de parcourir le DOM
  • Ne peut pas exécuter du Javascript
  • Rapide
Bien, mais limité...

Headless Browser


Headless Browser


Objectifs : utiliser un navigateur, en lui retirant la partie graphique en vue d'automatiser les process

Différentes implémentation : certains nécessitent l'installation des navigateurs sur le serveur de test (ou CI) ainsi que des drivers permettant de faire communiquer l'outils de test avec les navigateurs (Selenium, Sahi)

D'autres ont un seul navigateur et embarque tout dans un binaire (PhantomJS)

Passons maintenant à du concret

Selenium IDE :

Le BDD


ou plutot le Behavior Driven Development

exemple : Behat (PHP), Jasmine (JS), ...

Principes

  • issue des méthodes Agiles
  • incite à communiquer entre les intervenants du projet (donneur d'ordre, développeur, cp...)
  • utilisation d'un langage naturel
  • permet de définir les points essentiels du développement
  • séparation des rôles : les rédacteurs des tests ne sont pas les développeurs de l'application

Exemple



Scenario: List 2 files in a directory
  Given I am in a directory "test"
  And I have a file named "index.html"
  And I have a file named "composer.json"
  When I run "ls"
  Then I should get:
    """
    index.html
    composer.json
    """

CasperJS


Framework JS
Repose sur PhantomJS

Principe :
On écrit des tests en Javascripts en décrivant les étapes successives (open, thenOpen, thenEvaluate, thenClick, waitForSelector,...)
Ces tests vont être injectés avec CasperJS dans PhantomJS

PhantomJS


Un "simple binaire" disponible sur Linux/Win/Mac
Embarque un QtWebKit, soit le moteur du navigateur Chrome (et d'autre) codé à partir du framework Qt

PhantomJS


Avantages : 
  • un vrai navigateur est exécuté à chaque test
  • rapidité de mise en place : decompression d'un tar et c'est prêt
  • beaucoup plus rapide que les systèmes nécessitant l'installation de navigateur sur le serveur (Selenium, Sahi)

Inconvénients :
  • On ne test le rendu et le comportement que sur WebKit, et pas IE/FF/Chrome/Safari/Opera...
  • Depuis la 1.8, le produit est instable : Segmentation fault !!!
    Dur d'arriver au bout des tests
  • Bonne connaissance de Javascript requise

CasperJS dans le détail


Ensemble de fonctionnalités :
  • API : click, back, capture...
  • Client-side (auto injecté dans phantom) : echo, findAll, visible, ...
  • Colorizer (pour la sortie de la console) : colorize, format
  • Tester (un ensemble d'assertion) : assertEquals, assertTitle, ...
  • Utils : isNumber...

Demo - capture.js


#1 init pour PhantomJS : 

    phantom.casperPath = '.\casperjs';
    phantom.injectJs(phantom.casperPath + '\\bin\\bootstrap.js');
            
#2 le script

    casper.start('http://www.clubic.com', function capture() {
        this.capture('google.png');
        this.exit();
    });

Demo - CasperJS


#3 Devinez !

casper.run();
            

#4 En ligne de commande executez le script

phantomjs monScript.js
            

Autres possibilité :


#2 le script

casper.start('http://www.clubic.com',
    function captureOnFail() {
        if (!this.exists('#uneDivQuiExistePas')) {
            this.capture('google.png');
        }
        this.exit();
    });
            

Mais encore


#2 le script

casper.start('http://www.clubic.com',
    function captureOnFail() {
        this.test.assertTitle('');
        this.test.assertRessourceExists('all.js');
        if (!this.test.getFailures()) {
            this.capture('google.png');
        }
        this.exit();
    });
            

CasperJS chez M6


Mise en place d'un process :
  • Modularité des tests
  • Structure par projet
  • Execution des tests par projet, ou par fichier de test
  • Test des réponses HTTP automatique
  • Test des erreurs PHP standard (Fatal, Warning, page blanche)
  • Récupération des erreurs JS
  • Héritage : test du contenu (Fatal, Warning,...)
  • Tests executables par environnement (dev, trunk, master, prod)

Tests existant :


Backstage, Bbox : Behat/CasperJS
Clubic : CasperJS
MinuteFacile : CasperJs
Deco : CasperJS
M6.fr
M6corpo
Jvfr
social

Exemple pour M6


Projet à récupérer : tests-casperjs

Un coup de composer installl

Tout passe par un script : websitechecker.js

Les scripts de tests sont rangés par répertoire projet :
./modules/nomDuProjet (clubic, bbox, m6fr...)

Au sein de ces répertoires on crée un script par jeu de test:
./check_Actu.js

Ensuite :


Possibilité de tester en dev/trunk/master/prod via le paramètre --mode

Lancement des tests de manière unitaire ou par lot via le paramètre --project=nomProjet[/nomScript]

Et fin :


Il existe un script de démo : 
./modules/moduleSample.js

Il y a des fonctionnalités de base :
  • contrôle de la réponse HTTP
  • vérification des erreurs PHP (page blanche, Fatal, Warning...)
  • récupération des metaOg
  • récupération des erreurs JS (seulement la 1ère pour l'instant)
  • logComplexObject

Enfin presque:

ClubicChecker_Actu.prototype.launchTest = function (context) {
    "use strict";
    try {
        // @TODO finish test
        context.log('test not finished to define', 'info');
        if (ClubicChecker_Actu._super.launchTest.call(this, context)) {
            // retreive something from Phantom : context.evaluate(yourCallBackExecutedInPhantom);
            
            // do your test here with context.test. ()
            
        } else {
            context.echo('test stopped for module ClubicChecker_Actu');
            context.log('some errors has already been detected, launchTest discontinued', 'info');
        }
    } catch (e) {
        require("utils").dump(e);
        return e;
    }
};

test des metaOg :

ClubicChecker_Author.prototype.launchTest = function (context) {
    try {
		if (ClubicChecker_Author._super.launchTest.call(this, context)) {
			var metas = [], expected = {url: this.testUrl.replace('https:', 'http:')};

			metas = context.evaluate(this.getMetaOg);

			this.checkMetaOg(metas, expected);
			this.checkMetaFb(metas);
			this.checkMetaTwt(metas, expected);
			this.checkCanonicalUrl(metas, expected);
			this.checkPage();
		} else {
			context.echo('test stopped for module ClubicChecker_Author');
            context.log('some errors has already been detected, launchTest discontinued', 'info');
		}
	} catch (e) {
		require("utils").dump(e);
		return e;
	}
};

Bilan

  • Prévoir un budget pour les tests
  • Maintenir les tests
  • Casper c'est super, mais c'est pas encore très stable
  • Vous allez vous améliorer en Javascript

Merci

à la LFT
à RevealJs
à NodeJS
à AppFog pour l'hébergement
et surtout à Mickael !
Made with Slides.com