
Codeception - Comment démarrer les tests automatiques
Si vous avez lu nos articles précédents, vous savez déjà très bien comment démarrer un projet dans la console Docker. Si vous ne l'avez pas encore fait, vous devriez commencer par cet article, car aux fins de cet article, nous supposons que votre projet dans la console Docker est déjà opérationnel, donc toutes les commandes exécutées ci-dessous s'y référeront. Dans cet article, nous souhaitons vous introduire dans le monde des tests automatiques utilisant Codeception, basé sur ce type de projet. Bien sûr, tout le monde n'est pas obligé d'automatiser tous les tests sur leurs projets, mais si cela ne nécessite pas trop de travail, nous parions que beaucoup de gens verront d'un bon œil un ensemble de “tests fumée”, pour dire le moins.
Chez Droptica, nous essayons d'être la meilleure agence Drupal du marché, et nous croyons fermement que les tests automatisés nous aident à atteindre cet objectif. Ils réduisent drastiquement la quantité d'erreurs. Nous maintenons des projets dans lesquels pas une seule erreur ne s'est produite en production pendant des mois. Mais assez parlé de nous. Voyons comment utiliser Codeception.
Qu'est-ce que Codeception ?
Codeception est un framework utilisé pour créer des tests, y compris des tests unitaires, des tests fonctionnels et des tests d'acceptation. Malgré le fait qu'il soit basé sur PHP, l'utilisateur n'a besoin que de connaissances de base pour commencer à travailler avec le framework, grâce à l'ensemble de commandes personnalisées offertes par Codeception. Bien sûr, plus vous voulez tester, plus vous devriez en apprendre sur le développement PHP et la structure des tests automatiques. Dans Codeception, les tests sont écrits dans un style BDD (Behaviour Driven Development) – un ensemble de petites histoires qui nous racontent le comportement du système lorsqu'un événement spécifique se produit.
Installation
Nous avons déjà une image Codeception prête, vous n'aurez donc rien à installer. Téléchargez simplement les fichiers pour notre projet (bien sûr, par “notre projet”, j'entends ici le projet créé sur la base de l'article sur les bases de la console Docker). Pour ce faire, exécutez simplement la commande suivante dans le dossier du projet :
docker-console init-tests --tpl drupal7
ou pour un projet avec Drupal 8
docker-console init-tests --tpl drupal8
Lors de l'exécution, cela créera un dossier “tests” avec un environnement de test installé et prêt à l'emploi. La configuration restante sera la même pour Drupal 7 et 8 (sans compter les modules supplémentaires écrits pour la version concrète).
Configuration générale
Les données de configuration générale sont stockées dans le fichier codeception.yml, qui devrait ressembler plus ou moins à l'exemple ci-dessous. En utilisant ce fichier, nous pouvons configurer les chemins du projet, augmenter les limites de mémoire, définir une configuration par défaut pour un module spécifique, etc. À ce stade, vous n'avez rien à configurer ici pour que le projet fonctionne correctement.
Configuration détaillée
En outre, en dehors du fichier de configuration principal, chaque suite de test dispose de son propre fichier de configuration, tel que acceptance.suite.yml. Ici, nous créons une classe de testeur, qui spécifie les modules exacts que nous pouvons utiliser lors d'un test et – si nécessaire – nous permet de remplacer les paramètres du fichier général. Nous n'avons pas à modifier quoi que ce soit pour le moment ici ; cependant, j'aimerais souligner les paramètres de PhpBrowser. L'URL (http://web) définie ici n'a pas besoin d'être modifiée, car le nom “web” fait référence au nom de notre conteneur Docker, qui héberge notre site. La variable “auth” se réfère au nom d'utilisateur et au mot de passe que nous pouvons utiliser pour sécuriser notre site en utilisant .htaccess. Comme nous n'utilisons pas de telles mesures de sécurité dans notre projet pour le moment, vous pouvez supprimer cette ligne du fichier de configuration. La discussion des modules restants est un sujet beaucoup plus complexe et étendu, que je vais essayer de couvrir dans l'article à venir.
Une brève introduction aux tests
Comme je l'ai déjà mentionné plus tôt, avec Codeception vous pouvez écrire des tests unitaires, fonctionnels et d'acceptation.
Dans notre cas, nous avons divisé ces derniers selon le pilote utilisé. La principale raison d'une telle division était le temps d'exécution du test plus rapide dans le cas de l'utilisation de PhpBrowser et le besoin d'utiliser WebDriver pour les tests où des fonctions JavaScript étaient utilisées sur le site.
PhpBrowser |
WebDriver | |
---|---|---|
Moteur de navigateur | Guzzle + Symfony BrowserKit |
Chrome ou Firefox |
JavaScript | NON | OUI |
Visibilité des éléments sur le site |
Le texte est visible s'il est inclus dans le source |
Le texte est visible seulement s'il est visible pour l'utilisateur visitant le site |
Lecture des en-têtes HTTP de réponse |
OUI | NON |
Vitesse de test | Modérée | Lente |
Le tableau ci-dessous montre les différences générales entre divers types de tests. Nous avons également décidé d'ajouter des tests JS_capable, qui sont également inclus parmi les tests d'acceptation ; cependant, contrairement à la suite d'acceptation exécutée en utilisant PhpBrowser, les tests JS_capable sont lancés en utilisant WebDriver dans le navigateur web Chrome ou Firefox (comme je l'ai mentionné précédemment, c'est notre configuration et bien sûr, rien ne vous empêche d'ajouter WebDriver à la suite d'acceptation).
Unitaire | Fonctionnel | Acceptation | JS_capable | |
---|---|---|---|---|
Portée | Classe PHP | Framework PHP | Site ouvert dans un navigateur web (HTML visible) |
Site tel que vu par l'utilisateur final |
JavaScript | NON | NON | NON | OUI |
Serveur web requis | NON | NON | OUI | OUI |
Vitesse de test | ÉLEVÉE | ÉLEVÉE | MOYENNE | BASSE |
Fichier de configuration | unit.suite.yml | functional.suite.yml | acceptance.suite.yml | js_capable.suite.yml |
Sélecteurs
La dernière chose dont je voudrais parler avant que nous ne passions à l'écriture d'exemples de tests est les localisateurs, qui représentent la façon dont Codeception trouve les éléments que nous voulons utiliser pendant nos tests. Dans Codeception, vous pouvez trouver les éléments en utilisant :
- id, par exemple, ‘#test’, qui correspond à <div id=”test”>
- classes, par exemple, ‘.test’, qui correspond à <div class=”test”>
- noms, par exemple ‘test’, qui correspond à <div name=”test”> ou <input value=”test”> ou simplement le texte visible sur le site.
- CSS, par exemple 'input[value=test]' qui correspond à <input value="test">
- XPath, par exemple //input[@type='submit'][contains(@value, 'test')]"], qui correspond à <input type="submit" value="foobar">
Vous pouvez aussi utiliser des localisateurs plus complexes, qui sont décrits plus en détail ici : http://codeception.com/docs/reference/Locator
Quel que soit le moyen que vous utilisez pour localiser votre élément, vous devez toujours veiller à le faire de la manière la plus explicite et non ambiguë possible.
Tests d'acceptation
Si vous avez des cas de test que vous devez toujours passer avant de déployer le site afin de voir s'il fonctionne correctement, ce que vous avez réellement entre vos mains, ce sont des candidats idéaux pour l'automatisation et le placement des tests dans le dossier de test d'acceptation de Codeception. Dans le cas de notre configuration, le dossier d'acceptation contient tous les tests qui peuvent être effectués sans utiliser JavaScript, car dans ce cas, nous utilisons PhpBrowser (ce n'est pas nécessaire, mais certainement plus rapide). Avec ces tests, nous disposons de nombreuses fonctions utiles et déjà définies, dont la liste et les descriptions que vous pouvez trouver sur le site : http://codeception.com/docs/modules/PhpBrowser
À titre d'exemple, nous avons ici un test dans lequel nous nous connectons à un compte administrateur et vérifions si le texte de l'un des articles est visible sur la page d'accueil.
<?php
class ExampleAcceptanceTestCest
{
public function _before(AcceptanceTester $I) {
}
public function _after(AcceptanceTester $I) {
}
/** TESTS */
/**
* @param \AcceptanceTester $I
*
*/
public function exampleTest(AcceptanceTester $I) {
$I->wantTo('Test - Je peux me connecter en tant qu'admin et voir l'article');
$I->amOnPage('/');
$I->fillField('#edit-name', 'admin');
$I->fillField('#edit-pass', 'admin');
$I->click('Se connecter');
$I->amOnPage('/');
$I->see('Droptica se plonge dans les racines de 2008, lorsque l'un des cofondateurs a créé sa première entreprise de développement openBIT');
$I->amOnPage('/user/logout');
}
}
Tests JS_capable
En outre, pour nos besoins, nous avons ajouté la suite js_capable, où nous écrivons des tests d'acceptation nécessitant JavaScript. En général, nous devrions être en mesure de copier le test à partir du dossier d'acceptation, de le coller dans js_capable et il devrait fonctionner immédiatement ; cependant, nous devons nous rappeler que dans ce cas, le programme voit chaque élément exactement comme s'il s'agissait d'un utilisateur, avec un display:none ajouté en CSS, cet élément disparaîtra de vue, tandis que dans le cas de PhpBrowser, il restera visible, car il est inclus dans le code HTML. Pour comparaison, vous pouvez lire la liste des fonctions disponibles pour WebDriver : http://codeception.com/docs/modules/WebDriver
Avant de vous montrer un exemple de test, je voudrais également discuter de la configuration de ces tests. Pour les lancer, vous n'avez pas besoin de WebDriver ou même d'un navigateur web installé localement, car tout est déjà dans un conteneur Docker. Juste après l'initialisation du projet avec les tests, l'environnement est prêt à les lancer dans Chrome avec le Webdriver le plus récent. Cependant, si vous souhaitez voir comment vos tests se déroulent dans Firefox, vous devez le changer à deux endroits : Tout d'abord, vous devez remplacer “chrome” par “firefox” dans le fichier js_capable.suite.yml.
Ensuite, vous devez changer l'image Selenium pour une qui contient Firefox. Vous pouvez le faire en modifiant dc_settings.py, situé dans un dossier console Docker, où vous devez remplacer “standalone-chrome” par “standalone-firefox”.
Vous pouvez en savoir plus à ce sujet ici :
https://hub.docker.com/r/selenium/standalone-chrome/
https://hub.docker.com/r/selenium/standalone-firefox/
Maintenant, il est temps pour un exemple qui ajoute des nœuds comme l'article et la page de base.
<?php
class JSCentreTestsCest
{
public function _before(JSCapableTester $I) {
}
public function _after(JSCapableTester $I) {
}
/** TESTS */
/**
* @param \JSCapableTester $I
*
*/
public function addArticle(JSCapableTester $I) {
$I->wantTo('Test - J'ajoute un article');
$I->amOnPage('/');
$I->fillField('#edit-name', 'admin');
$I->fillField('#edit-pass', 'admin');
$I->click('Se connecter');
$I->amOnPage('/node/add/article');
$I->fillField('#edit-title', 'Article de test');
$I->fillField('#edit-body-und-0-value', 'Texte de test dans le corps de l'article');
$I->click('#edit-submit');
$I->see('L'article Test article a été créé.');
$I->see('Texte de test dans le corps de l'article');
$I->amOnPage('/user/logout');
}
/**
* @param \JSCapableTester $I
*
*/
public function addPage(JSCapableTester $I) {
$I->wantTo('Test - J'ajoute une page de base');
$I->amOnPage('/');
$I->fillField('#edit-name', 'admin');
$I->fillField('#edit-pass', 'admin');
$I->click('Se connecter');
$I->amOnPage('/node/add/page');
$I->fillField('#edit-title', 'Page de base de test');
$I->fillField('#edit-body-und-0-value', 'Texte de test dans le corps de la page');
$I->click('#edit-submit');
$I->see('La page de base Test basic page a été créée.');
$I->see('Texte de test dans le corps de la page');
$I->amOnPage('/user/logout');
}
}
Si vous souhaitez en savoir plus sur les tests d'acceptation, vous devriez certainement visiter :
http://codeception.com/docs/03-AcceptanceTests
Tests fonctionnels
Les tests fonctionnels sont écrits d'une manière très similaire à celle que nous avons utilisée pour écrire nos tests d'acceptation. La principale différence est qu'ils n'ont pas besoin d'être lancés sur un serveur web, ce qui les rend beaucoup plus rapides. En outre, ils offrent des commandes supplémentaires qui permettent de tester des frameworks tels que Symfony, Laravel5, Yii2, Yii, Zend Framework 2, Zend Framework 1.x et Phalcon. En réalité, écrire des tests fonctionnels n'a de sens que si vous utilisez l'un de ces frameworks, à moins que vous n'écriviez les fonctions nécessaires vous-même.
Ci-dessous, je vais vous montrer à quoi peut ressembler un tel test.
Avant de commencer à rédiger un test, nous devons ajouter la possibilité d'utiliser le module Db pour les tests fonctionnels dans le fichier functional.suite.yml. Après cette modification, le fichier devrait ressembler à ceci :
Maintenant, nous allons écrire un test qui vérifie si la base de données contient des nœuds (l'ajout de nœuds pourrait également être inclus dans ce test).
<?php
use Drupal\Pages\Page;
use Codeception\Util\Shared\Asserts;
class ExampleFunctionalTestCest
{
use Asserts;
public function _before(FunctionalTester$I) {
}
public function _after(FunctionalTester $I) {
}
/** TESTS */
/**
* @param \FunctionalTester $I
*/
public function exampleTestOfText(FunctionalTester $I) {
$I->wantTo('Test - Je peux voir le nœud dans la base de données');
$I->haveInDatabase('node', array('type' => 'article', 'title' => 'WSZYSTKO, CO CHCIELIBYŚCIE WIEDZIEĆ O REKRUTACJI W DROPTICE'));
$I->haveInDatabase('node', array('type' => 'page', 'title' => 'Page de test'));
}
}
Vous pouvez en savoir plus sur les tests fonctionnels ici : http://codeception.com/docs/04-FunctionalTests
Tests unitaires
Si vous avez écrit des tests unitaires PHPUnit dans le passé, vous n'avez pas besoin d'apprendre quoi que ce soit de nouveau et vous pouvez continuer à utiliser la même syntaxe qu'auparavant.
Dans mon exemple, il sera nécessaire d'activer l'utilisation des commandes Drupal par afin de faire fonctionner correctement ce test. Pour cela, je devrai déverrouiller un module dans le fichier unit.suite.yml. Après cela, le fichier devrait ressembler à l'exemple ci-dessous :
Nous pouvons maintenant commencer à écrire un test qui vérifiera si un module de sauvegarde et de migration est activé dans Drupal.
<?php
class ExampleUnitTest extends \Codeception\Test\Unit
{
/**
* @var \UnitTester
*/
protected $tester;
protected function _before()
{
}
protected function _after()
{
}
/** TESTS */
public function testModulesEnabled()
{
$modules[] = 'backup_migrate';
foreach ($modules as $modules_name) {
$result = module_exists($modules_name);
$this->assertEquals(TRUE, module_exists($modules_name));
}
}
}
Si vous souhaitez en savoir plus sur les tests unitaires, vous devriez visiter : http://codeception.com/docs/05-UnitTests
Lancement des tests
Après avoir tous les tests écrits et prêts, il ne reste plus qu'à les lancer et à les voir fonctionner. Souvenez-vous qu'avant de lancer les tests, vous devez démarrer les conteneurs du projet (dcon up). Vous pouvez lancer les tests de plusieurs manières :
- tous les tests écrits par nous,
dcon test
- seulement un ensemble donné de tests, par exemple.
dcon test acceptance
- un fichier unique avec des tests, par exemple.
dcon test acceptance/ExampleFilet.php
- un seul test, par exemple.
dcon test acceptance/ExampleFile.php::ExampleTest
Après avoir lancé les tests en utilisant la commande “dcon test”, vous devriez voir les tests s'exécuter et, à la fin, voir quelque chose ressemblant à l'image ci-dessous.
Rapports
Bien sûr, Codeception ne vous laisse pas uniquement avec ce que vous voyez dans la console. Après avoir terminé les tests, le dossier de sortie contiendra un rapport au format XML et HTML. Après avoir cliqué sur le signe plus à côté d'un test donné, le rapport affichera toutes les étapes réalisées pendant ce test.
Essayons maintenant de changer quelque chose sur notre site ou dans nos tests dans le but d'obtenir une erreur. Comme vous pouvez le voir, chaque fois qu'une erreur se produit, Codeception marque l'étape exacte où l'erreur s'est produite. De plus, comme le test a été effectué dans un navigateur utilisant WebDriver, nous avons une capture d'écran jointe, ainsi qu'une courte description du problème. Si l'un des tests d'acceptation échoue, vous aurez un fichier HTML contenant le code au moment où l'erreur s'est produite.
Fichiers du projet
Vous pouvez exécuter les exemples décrits dans cet article en les téléchargeant depuis le dépôt de produits et en changeant la branche à codeception-start.
Dépôt du projet :
https://github.com/DropticaExamples/docker-console-project-example
Sauvegarde de la base de données :
https://www.dropbox.com/s/tcfkgpg2ume17r3/database.sql.tar.gz?dl=0
Fichiers du projet :
https://www.dropbox.com/s/hl506wciwj60fds/files.tar.gz
Conclusion
J'espère qu'après avoir lu ce texte vous saurez comment commencer votre aventure avec Codeception et que je vous ai au moins légèrement encouragé. À la fin, je vais vous donner quelques conseils et lignes directrices concernant l'écriture de tests :
- souvenez-vous toujours des affirmations dans les tests, car un test qui ne vérifie rien n'est pas un test,
- tentez toujours d'écrire des tests stables (choisissez des sélecteurs d'éléments de manière à permettre au test de les localiser explicitement – personne ne veut de faux positifs et négatifs,
- essayez d'écrire plusieurs petits tests, plutôt qu'un grand, car si quelque chose casse, les tests restants auront l'opportunité de consulter la page plus en profondeur,
- essayez d'écrire des tests indépendants, et si cela est impossible, informez le système (par exemple en utilisant @depends note) pour passer à un autre test en cas d'échec du test,
- Ne tentez pas d'automatiser tout ce que vous faites – c'est quelque chose pour un autre article ou même un livre, mais peu importe ce que vous faites, avant d'écrire chaque test vous devriez réfléchir à la nécessité du test,
- si vous réutilisez un morceau de code plus d'une fois, pensez à le transformer en une fonction distincte,
- essayez de ne pas utiliser de sélecteurs et d'URL fixes dans vos tests (utilisez le Page Object Pattern – nous essaierons d'écrire plus à ce sujet bientôt.)