Vu que je me veux de toucher un peu à tous et que je balance sur un peu tous les frameworks lorsque quelque chose ne me plait pas, autant également le faire sur Rails …
Tout en essayant, pour une fois, de faire quelque chose de vraiment structuré et pas juste trois paragraphes ou je raconte ce qui me passe par la tête sur le sujet.

Qu’est-ce qu’une fixture ?

Lorsque vous créer des tests unitaires et fonctionnels, il est primordial d’avoir des données en base, pour effectuer vos tests.
Il est assez lourd de créer manuellement ses entrées pour chaque test.
Du coup rails propose des fixtures. Dans un fichier yaml, vous placez toutes vos données.
Et elles sont entrées automatiquement en base (sans validation).

C’est sympa ça permet de gagner pas mal de lignes de code dans vos tests.

Mais pourquoi c’est pas bien ?

Chaque fichier de fixture correspond à une base de données.
Si vous créer un fichier users.yml, qui contient des utilisateurs, celui-ci ne pourra remplir des données que dans la base users du fait du nom du fichier.

Du coup lorsque votre application commence à grossir, cela devient assez rapidement le bordel. Vous avez beaucoup de données. Celles-ci deviennent illisibles.
Vous ne savez jamais si telle ou telle donnée est réellement utilisée ou non.
Si en prime vous avez rédigé certains tests de manière absolue et non pas relative, il suffit que vous ajoutiez un utilisateur pour que 15 tests échouent alors que votre code est toujours valide.

A moins de s’imposer une rigueur particulièrement extrême, on s’oppose donc à des grosses prises de tête et une hausse de la consommation d’aspirine lorsque l’application va grossir.

Alors quelle solution ?

La solution est simple : n’utilisez pas les fixtures :)
Cela ne signifie pas ne rien utiliser du tout bien évidemment ! Parce que … Haaaave you met Factory Girl ?
L’idée de ce plugin est assez simple. Vous définissez plusieurs factories, qui vont être l’équivalent d’une instance d’un objet.

Mais par la suite, c’est vous qui définissez quel factory vous souhaitez utiliser et quand. Vous pouvez donc créer de nouvelles factories sans avoir de problèmes de tests qui échouent pour des raisons idiotes.
Et puis ce qui est encore mieux, c’est que Factory Girl ne fonctionne pas que avec ActiveRecord. Les factories que vous créer sont des instances d’un objet, sans distinction.
Vous pouvez donc créer une factory pour un model qui ne dépends pas de ActiveRecord. Sympa pour les mocks :)

Et vous, vous utilisez quoi pour initialiser des données de test ?

Un petit article rapide. Lorsque vous testez vos applications, l’envoi d’email est toujours embettant. Vous n’avez pas envie de l’email parte vraiment et souhaitez vérifier qu’il pars correctement.

Du coup il faut utiliser un Mock, qui va simuler l’envoi d’email mais n’enverra, en fait, rien.
Et qui vous permettra tout de même de surveiller l’email envoyé et de vérifier qu’il est correct.

Commençons, dans notre vue, par envoyer notre email :
from django.core.mail import send_mail
def envoi(self):
  send_mail(
    'A propos des tests',
    'C\'est fun quand même',
    'from@example.com',
    'to@example.com'
)

Et testons notre méthode :
from django.core import mail
def test_send(self):
  response = self.client.post('/envoi')
  self.failUnlessEqual(response.status_code, 302)
  self.assertEquals(len(mail.outbox), 1)
  self.assertEquals(mail.outbox[0].subject, 'A propos des tests')

Ainsi lorsque nous lançons notre test, en supposant que l’url /envoi soit configurée pour diriger vers notre action envoi, celle-ci sera exécutée, l’email sera envoyé de manière fictive.
Et vous aurez accès à un tableau dans mail.outbox qui contiendra tous les emails envoyés avec leur contenu, leur sujet et les personnes à qui ils ont été envoyés.

A vous, par la suite, d’écrire les tests appropriés au bon fonctionnement de votre application.

Comme tout framework qui se respecte, Symfony possède une interface permettant d’écrire et exécuter des tests unitaires et fonctionnels.
Voyons un petit peu celle-ci.

Dans votre projet, vous avez le dossier test qui contient trois dossiers.

Bootstrap

Ce dossier contient deux fichiers. functional.php et unit.php.
Ils sont à inclure au début de chacun de vos tests et permettent d’instancier l’environnement.

Unit

Passons donc maintenant aux choses sérieuses avec les tests unitaires :)
Voici un exemple de tests :
require_once dirname(__FILE__).'/../bootstrap/unit.php';

$t = new lime_test(2, new lime_output_color());

$t->comment('is the valid() method valid?');
$t->is(Post::my_test(true), true, 'valid() return the argument we give him');
$t->is(Post::my_test(false), false, 'valid() return the argument we give him');

A côté de ce test, nous avons un modèle Post qui contient une méthode my_test, celle-ci étant on ne peut plus basique puisqu’elle retourne la valeur que l’on lui donne.

Pour commencer, vous constatez que en première ligne de nos tests, nous incluons le bootstrap des tests unitaires.

Ensuite nous créons notre nouveau test, en y signalant que nous aurons deux assertions.
$t = new lime_test(2, new lime_output_color());
La méthode comment() permet de placer un commentaire dans l’interface de test, facilitant ainsi le débugguage ensuite.

Puis vient le moment des tests à proprement parler.
$t->is(Post::my_test(true), true, 'valid() return the argument we give him');
Le test échouera si la valeur retournée par Post::my_test n’est pas égale à true.

De nombreuses méthodes sont disponibles en plus de is().

  • comment($msg) – Affiche un commentaire mais n’exécute aucun test.
  • ok($test, $msg) – Affiche le commentaire et vérifie que $test est vrai.
  • is($value1, $value2, $msg) – Affiche le commentaire et vérifie que $value1 est égal à $value2.
  • isnt($value1, $value2, $msg) – Affiche le commentaire et vérifie que $value1 n’est pas égal à $value2.
  • like($string, $regexp, $msg) – Affiche le commentaire et vérifie que $string respecte l’expression régulière $regexp<./li>
  • unlike($string, $regexp, $msg) – Affiche le commentaire et vérifie que $string ne respecte pas l’expression régulière $regexp.
  • cmp_ok($value1, $operator, $value2, $msg) – Compare les deux arguments avec l’opérateur.
  • isa_ok($value, $type, $msg) – Vérifie le type de l’argument donné.
  • isa_ok($object, $class, $msg) – Vérifie que l’objet est bien de la classe mentionnée.
  • can_ok($object, $method, $msg) – Vérifie la disponibilité de la méthode pour un objet ou une classe.
  • is_deeply($array, $array2, $msg) – Vérifie que deux tableaux ont les mêmes valeurs.
  • include_ok($file, $msg) – Vérifie qu’un fichier existe et qu’il est correctement inclu.
  • fail() – Echoue toujours. Utile pour tester des exceptions.
  • pass() – Passe toujours. Utile pour tester des exceptions.
  • skip($msg, $nb_tests) – Compte comme $nb_tests tests. Utile pour les tests conditionnels
  • todo() – Compte comme un test. Utile pour les tests restant à écrire.

Functional

Et les tests fonctionnels. Voici un exemple de test :
include(dirname(__FILE__).'/../../bootstrap/functional.php');
$browser = new sfTestFunctional(new sfBrowser());

$browser->get('/')
  ->info('1 - The culture is appropriately fr')
  ->with('user')
  ->isCulture('fr')

  ->info('2 - The response is 200 and the content has a title')
  ->with('response')
  ->begin()
  ->isStatusCode(200)
  ->checkElement('body', '/<h1>/i')
  ->end();

Comme précédemment, nous commencons par implémenter le bootstrap.
Puis nous instancions un objet sfTestFunctional, qui fera office de navigateur et s’occupera de faire les appels http et de faire nos validations.

Ensuite nous faisons une requête get sur l’url /.
Et nous entrons dans la vue culture, pour vérifier que la langue définie pas défaut est bien le français. Voir internationalisation

Ensuite nous passons dans la vue response, nous vérifions que le code HTTP est bien 200 et que la page contient une balise h1.
Puis nous fermons cette requête.

Il serait bien évidemment possible de faire d’autres requêtes sans réinstancier le navigateur. Il suffit de refaire un $browser->get('/chemin');

Par ailleurs, comme précédemment, de multiples méthodes sont présentes :

  • get($url, $parameters) – Fait une requête get
  • post($url, $parameters) – Fait une requête post
  • call($url, $method, $paramaters) – Permet de faire un appel en précisant sa méthode (utilisé pour les requêtes PUT et DELETE).
  • back() – Retourne à la page précédente dans l’historique
  • forward() – Va à la page suivante dans l’historique
  • reload() – Recharge la page courante
  • click($name, $arguments, $options) – Clique sur un lien ou un bouton
  • select($name) – Sélectionne un bouton radio ou une case à cocher
  • deselect($name) – Désélectionne un bouton radio ou une case à cocher
  • restart() – Redémarre le navigateur (et la session)

Ainsi que d’autres méthodes pour configurer le comportement du navigateur

  • setHttpHeader($header, $value) – Définit une entête HTTP
  • setAuth($username, $password) – Définit l’identification http (basique)
  • setCookie($name, $value, $expire, $path, $domain, $secure, $httpOnly) – Définit un cookie
  • removeCookie($name) – Supprime un cookie
  • clearCookie() – Supprime tous les cookies
  • followRedirect() – Suis la redirection (et retourne une exception si le lien n’en est pas une)

Une fois que la requête est correctement paramétrée, il vous faut définir la vue que vous désirez avoir et exécuter celle-ci.
Il existe cinq vues. Mais nous n’en verrons que deux dans cet article.

request

Cette vue apporte des méthodes permettant de tester la requête.

  • isParameter($key, $value)> – Teste qu’un paramètre est présent et que sa valeur est correcte
  • isFormat($format) – Vérifie le format de la requête
  • isMethod($method) – Vérifie la méthode de la requête
  • hasCookie($key, $exists) – Vérifie que le cookie existe
  • isCookie($key, $value) – Vérifie la valeur du cookie

response

Cette méthode permet de vérifier le contenu renvoyé par la page.

  • checkElement($selector, $value, $options) – Vérifie qu’un sélecteur CSS respecte les critères donnés
  • isHeader($key, $value) – Vérifie la valeur d’une entête HTTP
  • isStatusCode($code) – Vérifie le code HTTP renvoyé
  • isRedirected() – Vérifie qu’il s’agit d’une redirection

Autres vues

Nous avons donc vu les deux vues les plus couramment utilisées. Cependant il en existe trois autres, que nous ne détaillerons pas ici :

Pour finir, comme vous pouvez le constater, tester ses applications est assez aisé avec Symfony. Alors vous n’avez plus aucune raison de ne pas le faire :)

Simian, aka Similarity Analyser est un outil des plus merveilleux. Il analysera vos lignes de code et détectera des potentielles répétitions.

A cela, nous couplons un gem de Jean-Michel Garnier (qui était présent au RubyCamp Lyonnais), Don’t Repeat Yourself.
Et à partir de maintenant, nous pouvons tester que nos applications Ruby et Rails sont DRY !!
J’ai testé et ça fonctionne plutôt du tonnerre.

Installation du plugin :
Si vous n’avez pas encore le catalogue de gems de GitHub, ajoutez-le :
gem sources -a http://gems.github.com
Puis installez le gem.
gem install dmathieu-dont_repeat_yourself
Vous constaterez que je vous fait installer non pas la version de Jean-Michel, mais la mienne. Les explications viennent plus bas.

Une fois que cela est fait, vous pouvez utiliser le plugin de trois manières possibles.

Dans vos tests

Avec Test::Unit

Créez le test suivant :
test "we don't repeat ourself" do
  assert_dry(rails_application.with_netbeans_reporting)
end

Avec RSpec

Créez le test suivant :
describe "Dupplicate lines Report: Don't Repeat Yourself" do
  it { ruby_project(File.dirname(__FILE__) + '/../').
    with_threshold_of_duplicate_lines(4).
    with_netbeans_reporting.
    should follow_the_dry_principle }
end

En ligne de commande

Tapez la commande suivante
dry-report

Ces rapports DRY sont même encapsulables dans Netbeans ou Textmate. Mais je n’ai pas encore penché mon nez là-dedans. Donc je vous laisse regarder ;)

Vous constaterez par ailleurs que je vous invite à installer le fork que j’ai fait du gem et non pas la version originelle.
La raison à cela est que en Rails 2.3, Test::Unit deviens ActiveSupport::TestCase.
Cette modification n’a pas encore été appliquée au gem originel. Mais je suis persuadé que cela ne saurait tarder ;)

J’ai déjà mentionné Selenium dans mon article de test de code avec JQuery.

Nous allons voir aujourd’hui, en introduction à celui-ci, comment tester votre présence sur Google pour les mots clés de votre choix.
Ceci est cependant un cas d’école. Vous ne pouvez tester (simplement) votre positione exacte. Nous vérifions ici uniquement que vous êtes dans les 10 premiers. Mais il faut bien un exemple pour débuter et vous permettre ensuite de tester vous-même votre site.

Commencez par télécharger et installer Selenium IDE, qui est une extension firefox permettant de générer des tests Selenium.
Une fois celui-ci installé, rendez-vous dans le menu « Outils » du navigateur puis cliquez sur « Selenium IDE ». Une fenêtre s’ouvrira alors, vous permettant de créer votre nouveau test.

De multiples commandes sont disponibles. Ici cependant, nous désirons ouvrir google.com, puis entrer une recherche, cliquez sur le bouton et analyser le résultat.

Notre set de commandes sera donc :

Test google.com Selenium

open /
type q dmathieu
clickAndWait btnG
assertTextPresent dmathieu.com

Exécutez votre test et c’est magique. Vous voyez même le test s’exécuter en direct dans votre fenêtre active :)
Maintenant, c’est bien gentil, vous avez votre test. Mais comment l’automatiser ?

Eh bien vous avez le choix. Java, C#, Perl, PHP, Python, Ruby ?
Il n’y a qu’à choisir, installer la librairie du langage.
Puis faites Fichier > Export Test Case as … Et choisissez le langage de votre choix.

Exécutez le script généré. Et si vous avez correctement installé les librairies, votre test devrait s’exécuter.
Vous avez réalisé votre premier test frontend automatique. Vous n’avez maintenant plus qu’à adapter cela à vos tests QUnit (ou tout autre framework) pour automatiser ceux-ci.

Note : Tester votre présence sur Google de cette manière est assez peu productive.
Pour cela, je vous conseille plutôt RefStats.

Je viens de publier un article sur le blog d’O2Sources, sur le thème suivant : « Ecrire du code testable« .
En effet, vous pouvez avoir toute la bonne volonté du monde à vouloir écrire des tests dans votre code, si celui-ci n’est pas écrit de manière à vous permettre de créer des tests de manière aisée, vous n’arriverez à rien.

Cet article donne donc une liste de 10 conseils pour écrire du code testable.

  1. Ne pas mélanger le constructeur avec le code logique
  2. Demandez les choses. N’allez pas les chercher vous même
  3. Ne faites pas tout le travail dans le constructeur
  4. Evitez les singletons et variables globales
  5. Evitez les méthodes statiques
  6. Favoriser les compositions aux héritages
  7. Favoriser le polymorphisme aux conditions
  8. Mélanger des objets de valeur et des objets de service
  9. Ne mélangez pas tout
  10. Pensez deux fois, n’agissez qu’une

Je vous invite donc à lire cet article « Celui qui voulait rendre son code testable« .

J’ai déjà parlé plusieurs fois ici de tests unitaires. En Ruby ou en PHP.
Cependant toute application qui se respecte aura également une couche navigateur, permettant d’ajouter de l’ajax par exemple.
Ces fonctionnalités, tout comme le code serveur, sont testables.

Voyons un petit peu comment cela fonctionne avec QUnit, le framework de tests unitaires intégré avec JQuery.

Pour cela, je vais reprendre mon exemple d’il y a quelques mois ou je proposais de signaler si un pseudonyme est déjà utilisé lors d’une inscription, et de tester cela.
Cependant aujourd’hui, j’ai fait la chose en PHP, afin d’épargner la création d’un projet RoR. On a au final, dans cet exemple, besoin que de deux fichiers :

check_user.php, qui vérifie si notre pseudonyme est disponible ou non. Et retourne ‘OK’ si il l’est et ‘NOT’ sinon.
On ne s’embête pas ici. Voici ce que j’ai dans mon document :

<?php if ($_GET['u'] == 'oh') echo 'OK';
else echo 'NOT'; ?>

Puis dans notre index.php, nous allons placer le champ de texte du nom d’utilisateur.

<html>
    <head>
        <title></title>
        <script src="http://www.google.com/jsapi"></script>
        <script type="text/javascript>
            google.load("jquery", "1.3.2");
        </script>
        <script type="text/javascript" src="code.js"></script>
    </head>
    <body>
        <input type="text" id="username" name="username" />
        <div id="availability"></div>
    </body>
</html>

Et nous appellons notre document javascript qui fera un appel ajax à check_user.php.

$(document).ready(function() {
    $('#username').blur(function () {
        username = $('#username').val();
        if (username == '') {
            $('#availability').html('');
            return false;
        }

        $.ajax({
            url: 'check_user.php?u='+username,
            success: function(data) {
                    if (data == 'OK') {
                        $('#availability').html('Disponible');
                        $('#availability').css('color', 'green');
                    } else {
                        $('#availability').html('Non disponible');
                        $('#availability').css('color', 'red');
                    }
            },
            error: function(data) {
                $('#availability').html('');
            }
        });
    })
});

Voila ! Maintenant, nous avons notre test de validité de l’utilisateur de fonctionnel. Pour l’instant du moins car pour être sur qu’il le reste, nous allons ajouter des tests.
Commençons par implémenter QUnit dans notre index.php.

<html>
    <head>
        <title></title>

        <script src="http://www.google.com/jsapi"></script>
        <script type="text/javascript">
            google.load("jquery", "1.3.2");
        </script>
        <script type="text/javascript" src="code.js"></script>
    </head>
    <body>

        <input type="text" id="username" name="username" />
        <div id="availability"></div>

        <link rel="stylesheet" href="http://github.com/jquery/qunit/raw/master/qunit/qunit.css" type="text/css" media="screen" /
	<script type="text/javascript" src="http://github.com/jquery/qunit/raw/master/qunit/qunit.js"></script>
        <script type="text/javascript" src="tests.js"></script>
        <h2 id="qunit-banner">Qunit Testing</h2>
	<h2 id="qunit-userAgent"></h2>
	<ol id="qunit-tests"></ol>
    </body>
</html>

Vous pouvez constater que nous ajoutons divers tags html en fin de page. Ceux-ci seront automatiquement remplacés par l’interface de visualisation des tests.

Et enfin dans le fichier tests.js inclus précédemment, nous plaçons tous nos tests.

$.ajaxSetup({
  async: false
});

module("username")

test("availability div shouldn't have any value by default", function() {
    equals($('#availability').html(), '', "availability div doesn't have any value by default");
});

test("should allow inexistant username", function() {
    $('#username').val('oh');
    $('#username').blur();

    equals($('#availability').html(), 'Disponible', "the username is available");
    equals($('#availability').css('color'), 'green', "the username is available");
});

test("should not allow existant username", function() {
    $('#username').val('doh');
    $('#username').blur();

    equals($('#availability').html(), 'Non disponible', "the username is not available");
    equals($('#availability').css('color'), 'red', "the username is not available");
});

test("should hide the message if username is empty", function() {
    $('#username').val('');
    $('#username').blur();

    equals($('#availability').html(), '', "the username is empty");
})

La méthode « module » permet de signaler que nous sommes dans la partie de vérification du pseudonyme de la page.
Chaque méthode test définit un test.

Quant au ajaxSetup, il nous permet de forcer les requêtes ajax à ne pas être asynchrone. Si elles l’étaient, nos tests seraient exécutés avant le retour de la requête ajax et échoueraient donc.

Si vous rechargez votre page, vous pouvez maintenant voir que nous testons convenablement (et avec succès) la vérification de la disponibilité de notre pseudonyme.

Pour finir, vous me direz rapidement que c’est bien gentil de tester ça. Mais que vous n’allez pas charger chacune des pages de votre site à chaque fois que vous changez une ligne de code.
La solution à cela, qui est cependant trop complexe pour tenir dans cet article, est d’utiliser Selenium, un outil permettant de tester le frontend d’une application web sur de multiples navigateurs.

Pour finir, vous pouvez voir toutes les portions de code données dans cet article avec mise en couleur du code.

Certains le savent déjà, j’ai acheté il y a un mois de cela un nouvel objectif, le Tokina 11-16mm.

Ce test ne se veut absolument pas technique. Pour cela, je vous invite à lire le test fait par Jean-Marie Sépulchre pour le monde de la photo.
Il s’agit ici d’un test plus « pratique ». Ce que j’ai fait avec, ce qu’il permet de faire, ce pour quoi il est génial et les moments ou il n’est pas à utiliser.

Ce billet sera agrémenté de multiples photos, toutes prises avec le Tokina.

Tout d’abord, 11-16, ultra grand angle. En gros, n’espérez pas faire de gros plan avec. Même si vous prenez une photo à 30 centimètres de quelqu’un, vous verrez sa tête en entier ainsi qu’une partie de son corps.

Il faut également fortement se méfier. En effet, avant d’acheter l’objectif, je me disais « ça va être super pour les paysages ».
Cependant ces paysages se retrouvent rapidement très chargés car trop d’éléments se retrouvent sur la photo.
L’oeil et le cerveau n’arrivent alors plus à s’y retrouver et la photo n’est que peu intéressante.

Quelles sont les photos intéressantes à prendre avec alors me direz-vous ? Eh ben j’ai envie de te dire « toutes les autres » :-)
Prendre une rue pleine de passant(e)s; un objet seul, ou encore un portrait.

L’objectif ouvre à 2.8 (c’était un argument majeur. Je commence à réflechir à l’achat d’un 70-200mm et je pense me tourner vers le Canon pour son ouverture à 2.8 (mais l’achat ne se fera probablement pas avant janvier prochain)).

Et cette grande ouverture est un élément indispensable.
Ainsi la photo suivante a été prise à 11mm avec une ouverture à 2.8 … de nuit et sans aucune lumière autour (ou presque). Et sans flash, cela va de soi.

Evidemment elle a subi un coup de photoshop afin de l’éclaircir. Mais pour exemple, en voici une autre prise au même endroit à 16mm, 2.8 également et sans retouche au niveau de l’éclairage.

Finissons-en des banalités avec l’ouverture et le zoom et passons aux choses amusantes.
Vu l’ouverture qu’il a, les photos qui sont les plus intéressantes à prendre sont celles faites de dessus ou de dessous.

En effet avec le grand angle mais sans déformation (ou presque), l’effet est différent de celui obtenu avec un FishEye. Mais se retrouve tout de même franchement intéressant.
Je n’ai pas de photos de dessous, j’ai toujours préféré être au dessus ;)
Mais l’effet est similaire.

Pour finir, cet objectif a une qualité de fabrication assez exceptionnelle (normal remarque, c’est un tokina).
Il faut juste ne pas se laisser impressionner par les petites « tâches » noires dessus qui sont normales et qui ne sont pas des gouttes d’eau.

 
Fork me on GitHub