Fil d'Ariane
Porter son module en D8 - #3 Encore les tests
Cet article est le troisième de la série qui suit, étape par étape, la conversion du module Multiple E-mail Addresses à Drupal 8. Il complète le second dans le processus de réécriture des tests fonctionnels existants.
Dans le précédent article de cette série qui suit, étape par étape, la conversion d'un module contribué, Multiple E-mail Addresses, de Drupal 7 vers Drupal 8, nous avons commencé à convertir les tests fonctionnels afin d'être certains de ne pas introduire de régression. Maintenant que nos tests peuvent être exécutés par Drupal, nous allons aborder quelques unes des erreurs les plus courantes qu'il faudra corriger avant même de s'attaquer au côté fonctionnel de votre portage.
Les dépendances
Cette première erreur est très souvent assez insidieuse. Les tests ayant pour objectif de valider le fonctionnement d'un ou plusieurs modules dans un contexte donné, ils ont presque toujours besoin de définir ces derniers en dépendances qui seront auromatiquement activées au lancement du test. Or, ce système de déclaration des dépendances a légérement changé et il n'est pas forcément évident de s'en rendre compte en se basant uniquement sur les erreurs remontées par Simpletest.
Par exemple, en exécutant la classe de tests UserTest à nouveau, nous obtenons une erreur "Fatal error: Call to a member function getUsername() on boolean in /core/modules/simpletest/src/WebTestBase.php on line 553". Celle-ci se produisant au sein même du code de Simpletest, lui même très bien testé, nous devons supposer que l'erreur est en réalité issue de notre code. Selon le message d'erreur, cette dernière a lieu au sein de la méthode drupalLogin() de la classe WebTestBase. Que ce soit à l'aide d'un debugger ou par le biais de méthodes plus artisanales (un var_dump() du retour de la fonction debug_backtrace() par exemple), nous constatons que l'erreur est soulevée à partir de l'appel à la méthode drupalLogin() contenu dans notre méthode setUp(). Cela suffit pour nous donner des clefs pour faire une recherche dans les Change records avec le mot clef "setUp" et pour trouver une référence au fait que la méthode pour indiquer les dépendances d'un module a changé.
Le Change record nous indique qu'il faut désormais indiquer les dépendances dans une propriété statique $modules de la classe au lieu de les passer à la méthode setUp() de la classe parente. On remplace donc
function setUp() {
// Enable the Multiple User module
parent::setUp('multiple_email');
// Create a user allowed to have multiple emails.
$this->user = $this->drupalCreateUser(array('use multiple emails'));
$this->drupalLogin($this->user);
}
par
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('multiple_email');
function setUp() {
// Set up basic Drupal install.
parent::setUp();
// Create a user allowed to have multiple emails.
$this->user = $this->drupalCreateUser(array('use multiple emails'));
$this->drupalLogin($this->user);
}
Malheureusement, cela ne suffira pas à corriger notre erreur. Cette dernière est dûe au fait que la permission que nous souhaitons attribuer à l'utilisateur n'existe pas car elle est censée être déclarée par notre module et que nous n'avons pas encore converti le hook_permission(). Afin d'avancer dans la conversion de nos tests, nous allons donc retirer cette permission et nous laisser un commentaire pour penser à la réintégrer lorsque nous commencerons à travailler sur cette partie du module. De toute manière, cette permission sera nécessaire pour faire passer les tests lorsque le module sera de nouveau fonctionnel avec tous ses contrôles d'accès.
function setUp() {
// Set up basic Drupal install.
parent::setUp();
// Create a user allowed to have multiple emails.
// @todo add 'use multiple emails' permission to $this->user ASAP.
$this->user = $this->drupalCreateUser();
$this->drupalLogin($this->user);
}
Autres erreurs bloquantes courantes
Les erreurs bloquantes sont celles qui interrompent le test et qui renvoient vers une erreur avant même d'afficher le rapport de test. Cette erreur étant renvoyée par le batch, le message n'est pas toujours très clair car le HTML qu'il contient est restitué en texte simple.
randomName()
Maintenant que nous avons retiré les permissions issues du module de la création des utilisateurs, nous pouvons constater de nouvelles erreurs. Par exemple, "Fatal error: Call to undefined method Drupal\multiple_email\Tests\UserTest::randomName() in /modules/multiple_email/src/Tests/UserTest.php on line 45" nous donne une nouvelle fois un résultat pertinent dans les Change records. Nous remplaçons donc toutes les occurences de
$this->randomName()
en
$this->randomMachineName()
Accès aux propriétés des entités
L'erreur suivante est plus difficilement lisible malgré le fait d'avoir désactivé le debugger. Il s'agit visiblement d'une erreur SQL soulevée lors d'une tentative d'injection de données. La requête semble attendre un simple id mais reçoit en fait un objet entier de type Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem. Cette fois-ci, même si un Change record existe bel et bien pour expliquer la raison de cette erreur, il ne sera pas aisé de le trouver. L'erreur est dûe à deux changements fondamentaux de la gestion des entités par Drupal 8. Premièrement, toutes les données d'une entité sont désormais des champs (Fields) soit définis de manière structurelle par l'entité (Base fields) et que l'on appelait propriétés soit, comme avant, ajoutés en fonction des besoins (Configurable fields). De plus, le cœur intègre désormais un équivalent des Entity Metadata Wrappers du module Entity API de Drupal 7 directement. Aini, $node->nid ne retournera plus l'identifiant du nœud mais un objet de type IntegerItem qui permettra d'accéder à sa valeur de la même manière qu'on accède aux valeurs de tous les autres champs. Selon la documentation il existe donc deux manières d'accéder à cette valeur, la première, générique, est $node->nid->value. La seconde, spécifique aux identifiants et plus généralement aux champs disposant de getters est $node->id().
Dans notre cas, les coupables sont
$this->loggedInUser->uid
$this->loggedInUser->mail
que l'on remplacera partout respectivement par
$this->loggedInUser->id()
$this->loggedInUser->getEmail()
Autres erreur non-bloquantes courantes
Les erreurs non-bloquantes sont celles qui apparaissent dans le rapport de test mais qui n'ont pas l'air d'être en rapport avec le fait que les tests échouent mais plus avec le fait qu'il y a un problème dans la manière dont les tests sont écrits.
drupalPost()
L'une des erreurs non-bloquantes la plus courante est liée à la méthode drupalPost() auparavant énormémént utilisée pour simuler des soumissions de formulaire. Elle prend généralement la forme d'une simple notice : "Argument 3 passed to Drupal\simpletest\WebTestBase::drupalPost() must be of the type array, string given, called in /modules/multiple_email/src/Tests/UserTest.php on line 136". Comme dans la plupart des cas précédents, ce sont les Change records qui vont nous apporter la raison de cette erreur. En effet, du fait que Drupal 8 se comporte maintenant nativement comme un web service REST, les requêtes de type POST ne sont plus réservées aux soumissions de formulaires. La méthode drupalPost() a été renommée drupalPostForm() et une nouvelle méthode drupalPost() a été créée pour permettre de tester les fonctionnalités liées aux webservices.
Nous remplacerons donc tous nos
$this->drupalPost()
en
$this->drupalPostForm()
Conclusion
À ce stade vous devriez avoir résolu la grande majorité des problèmes communs de vos tests et, dans le cas contraire, avoir compris que ce sont les change records qui vous donneront toutes les informations nécessaires pour faire tourner vos tests. Dans le prochain épisode, nous aborderons la Configuration Management Intiative par le biais de la conversion des variables Drupal en objets de configuration simple.
Votre commentaire
À propos de Edouard
Expert technique
Après un premier contact douloureux avec Drupal en 2009 en autodidacte, j'ai suivi une formation qui m'a convaincu de mon choix technologique et m'a vraiment mis en selle. Durant plusieurs années suite à cela j'ai accompagné des entreprises locales dans le développement de leurs projets de toutes sortes, de la simple vitrine à l'intranet social en passant par le projet e-commerce.