Tester le code développé

Tester le code est vraiment important.

La plupart du temps, des excuses sont invoquées telles que “je n’ai pas le temps” ou “il vaut mieux passer du temps sur telle fonctionnalité”. Ces excuses donnent lieu à une perte de temps en correction de régressions ou de bugs improbables par la suite.

Il existe plusieurs types de tests, à vous d’utiliser les types et formats de tests qui correspondent à votre mode de fonctionnement. On retrouve donc deux familles de tests :

  • les tests unitaires
  • les tests fonctionnels

Les tests unitaires vous permettent de vérifier le fonctionnement d’une méthode, la plus petite unité de code. Le principe est que vous envoyez des éléments en entrée de la méthode et vous comparez le résultat retourné avec le résultat attendu. Les tests unitaires doivent couvrir en priorité le code métier. Dans une application Symfony, ce code métier se situe dans les services et dans les repositories.

Les tests fonctionnels vous permettent de vérifier une fonctionnalité. Il s’agit souvent d’une suite d’appels HTTP dont les retours vont être inspectés. Mais celà peut être également une suite d’appels à l’API interne de l’application. Dans un application Symfony, le plus souvent ce sont les contrôleurs qui vont être appelés.

Behat

Behat est un framework PHP qui permet de rapidement mettre en place des tests. Le plus souvent, il est utilisé dans le cadre de tests fonctionnels.

Un de ses principaux avantages est l’écriture de scénarios dans un langage Gherkin qui est proche de l’anglais. Ainsi la production de scénarios est facile à réaliser et à lire pour tout le monde. Les intervenants, autres que les développeurs, peuvent alors écrire des tests et les ajouter au projet. De même si elles decouvrent ou qualifient un bug, elles devraient ajouter le scénario pour l’obtenir et placer la vérification avec la valeur attendue.

Un autre avantage est qu’il est en mesure d’utiliser Mink, un framework PHP open source qui expose une API permettant le contrôle de navigateurs à travers des pilotes. Cela permet de simuler le comportement d’un internaute qui consulterait le site.

Un peu de vocabulaire :

  • Scenario: il s’agit d’une suite d’étapes rédigées en langage Gherkin qui va procéder à des appels et effectuer les vérifications
  • Feature: il s’agit d’un regroupement de scénarios, le plus souvent sur le même thème
  • Context: c’est une classe qui permet d’étendre et enrichir le langage Gherkin
  • Extension: c’est une bibliothèque qui va ajouter des fonctionnalités supplémentaires à Behat

Exemple de scénario :

  Scenario: Add addresses to a campaign with a bad payload
    Given I send a POST json request to "/partner/v1/campaigns/1/tasks" with body:
    """
    {
      "add":[],
      "type":"IMPORT_ADRESSES"
    }
    """
    Then the response status code should be 400
    And the JSON should be like:
    """
    {
      "code": 400,
      "message": "Validation Failed",
      "errors": [
        {
          "property_path": "[addresses]",
          "message": "This field is missing."
        }
      ]
    }
    """

Installation dans Symfony

Pour ajouter Behat à votre projet Symfony, vous pouvez utiliser composer. Pour interfacer Behat et Symfony, l’extention Symfony2Extension est nécessaire. Cette dernière est prévue pour fonctionner avec le pilote BrowserKit pour Mink qu’il suffit d’activer.

Vous avez donc besoin de ces dépendances, à ajouter à celles de développement, dans votre fichier composer.json :

composer require --dev behat/behat \
                        behat/symfony2-extension \
                        behat/mink \
                        behat/mink-extension \
                        behat/mink-browserkit-driver

Il ne vous reste plus qu’à créer :

  • un fichier behat.yml à la racine de votre projet, c’est le fichier de configuration
  • un dossier features à la racine de votre projet, il contiendra les différents tests que vous allez rédiger

Configuration

Le fichier behat.yml vous permet d’ajouter de la configuration pour l’exécution des tests. Je vous invite à parcourir la documentation de référence.

Plusieurs points sont à définir dans la configuration :

  • le nom de domaine, en fonction de votre manière d’exécuter des tests, ce nom de domaine sera utilisé au début des appels que Mink effectuera
  • l’activation de l’extension Behat\Symfony2Extension qui permet de faire le lien entre Behat et Symfony
  • la configuration de l’extension Symfony précise qu’il faut utiliser le kernel de test

Exemple :

default:
  calls:
    error_reporting: 16383 # E_ALL & ~E_USER_DEPRECATED
  formatters:
      progress: ~
  suites:
    default:
      filters:
        tags: "~@skip"
      contexts:
        - Behat\MinkExtension\Context\MinkContext
  extensions:
    Behat\Symfony2Extension:
      kernel:
        env: "test"
        debug: "true"
    Behat\MinkExtension:
      base_url: "http://myproject.docker/"
      sessions:
        default:
          symfony2: ~

Ecriture d’une feature

Maintenant que tout est prêt, une nouvelle feature peut être ajoutée.

Pour l’exemple, on va créer un fichier article.feature qui contiendra un scénario qui consistera en un appel d’une page par Mink et on vérifiera le code de retour HTTP qui devra corresponde à 200.

@articles
Feature: Articles
  As an anonymous user, i should be able to read the articles

  Scenario: Access the list page
    Given I am on "/article/"
    Then the response status code should be 200

Au début du fichier on peut voir @articles, il s’agit d’un tag qui vous permet par la suite de n’exécuter qu’une partie des tests.

Les quelques descriptions ne sont là que pour la lisibilité des tests.

Utilisation en CLI

Pour les tests et obtenir le résultat :

vendor/bin/behat --colors -vvv

Pour obtenir un affichage plus détaillé, utilisez le format pretty :

vendor/bin/behat --colors -vvv -f pretty

À des fins de débogage, quand vous utilisez le format pretty, dans un scénario vous pouvez utiliser la commande suivante pour afficher le body de la dernière requête HTTP :

    Then print last response

Conclusion

Si vous exécutez les tests, vous devriez obtenir ce résultat :

@articles
Feature: Articles
  As an anonymous user, i should be able to read the articles

  Scenario: Access the list page                # features/Articles.feature:5
    Given I am on "/article/"                   # Behat\MinkExtension\Context\MinkContext::visit()
    Then the response status code should be 200 # Behat\MinkExtension\Context\MinkContext::assertResponseStatus()

1 scénario (1 succès)
2 étapes (2 succès)
0m0.15s (25.20Mb)

Bien sûr, vous pouvez maintenant aller plus loin en ajoutant d’autres features et en les faisant plus complexes, en vérifiant le code HTML pour savoir s’il contient bien les 10 articles des fixtures par exemple.