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, ...
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 !