Coder ses tests sans les mains grâce aux Live Templates de PhpStorm

| 6 min read

Je ne tape pas particulièrement vite, en tous clairement pas avec les 5 doigts des 2 mains[1] mais j’arrive à compenser parce que j’ai trouvé différentes manières d’écrire le même code que quelqu’un qui taperait vite tout en seulement quelques caractères.

Les Live Templates des IDE Jetbrains, et donc de PhpStorm, font clairement partie de mes fonctionnalités préférées.

Dans cet article je vais vous partager certains des Lives Templates que j’utilise lorsque je travaille avec des tests, ce qui devrait vous faire découvrir la puissance de cet outil.

On va commencer par des Live Templates simples et on verra sur la fin ce que l’on peut faire quand on commence à pousser l’outil presque au maximum[2] de ce qu’il est capable de faire.

Comment récupérer ces Live Templates

Pour chacun des exemples de cet article je vous partage le snippet XML qui le représente, ce qui va vous permettre de facilement le mettre en place dans votre IDE.

Pour cela :

  1. Copiez le snippet XML
  2. Dans PhpStorm, ouvrez les préférences et cherchez Live Templates
  3. Sélectionnez le dossier dans lequel vous voulez ajouter le Live Template. Sans doute Php ou PhpUnit pour ceux de cet article.
  4. Gardez la sélection sur le dossier et collez, avec CTRL + V ou CMD + V
  5. Adaptez-le si besoin !

Création de cas de test

J’utilise plusieurs Live Templates qui me permettent de créer facilement des cas de tests.

Créer un cas de test et commencer à tester

Un gif qui fait la démonstration du live template: "@t", tab, écrire le nom du test, tab, écrire le test

Le premier, @t, pour "test", est celui que j’utilise le plus souvent. Il aide à créer un test avec l’annotation @test, plutôt qu’avec le prefix test. Cela permet d’avoir des noms de tests plus lisibles.

<template name="@t" value="/**&#10;* @test&#10;*/&#10;public function $NAME$(): void {&#10;    $END$&#10;}" description="Add a test function" toReformat="false" toShortenFQNames="true">
  <variable name="NAME" expression="" defaultValue="" alwaysStopAt="true" />
  <context>
    <option name="PHP Class Member" value="true" />
  </context>
</template>

Créer un cas de test pour plus tard

Quand on fait du TDD on a parfois envie de lister des cas de tests en avance. Il y a plusieurs options: noter sur une feuille de papier, mettre des commentaires, ou créer des méthodes tests.

Pour faciliter la création de méthodes de test "pense-bête" j’ai un autre Live Template, @ts, pour "test skipped".

Un gif qui fait la démonstration du live template: "@ts", tab, ajoute un skipped test, tab, écrire le nom du test, tab, le curseur se place une ligne en dessous de la nouvelle méthode de test.

Notez qu’une fois le nom du test écrit, le curseur se place une ligne sous la nouvelle méthode de test pour permettre d’enchainer la prise de note des idées.

<template name="@ts" value="/**&#10;* @test&#10;*/&#10;public function $NAME$(): void {&#10;&#9;$this->markTestSkipped('Not implemented yet.');&#10;}&#10;&#10; $END$" description="Add a test function" toReformat="true" toShortenFQNames="true">
  <variable name="NAME" expression="" defaultValue="" alwaysStopAt="true" />
  <context>
    <option name="PHP Class Member" value="true" />
  </context>
</template>

Une grosse partie de ce qui fait la réussite d’un Live Template pour gagner du temps est l’endroit où se place le curseur une fois la modification terminée. Quand vous créer un Live Template vous pouvez spécifier la dernière position du curseur avec la variable $END$.

Lazy Naming des tests

Un dernier exemple de création de méthode de test.

J’ai envie d’expérimenter avec l’idée de Lazy Naming[3]. En Lazy Naming on ne va nommer les tests qu’une fois qu’ils sont dans leur forme finale, ou que l’on en est assez satisfait pour daigner faire l’effort de les nommer.

Le Live Template @tl, pour "test lazy", rajoute un nouveau test dont le nom est généré en utilisant le numéro de la ligne et le curseur est directement placé dans le corps de la méthode.

Un gif qui fait la démonstration du live template: "@tl", tab, une méthode de test avec un nom généré est ajoutée, et on peut écrire le test

<template name="@tl" value="/**&#10;* @test&#10;*/&#10;public function TODO_RENAME_$LINE_NUMBER$(): void {&#10;    $END$&#10;}" description="Add a lazy named test" toReformat="false" toShortenFQNames="true">
  <variable name="LINE_NUMBER" expression="lineNumber()" defaultValue="" alwaysStopAt="false" />
  <context>
    <option name="PHP Class Member" value="true" />
  </context>
</template>

Builders

Passons maintenant aux choses un peu plus sérieuses !

J’utilise énormément de builders dans les tests. Ils facilitent fortement la lisibilité et la maintenabilité des tests.

Le seul problème des builders c’est leur mise en place qui demande un peu d’investissement. C’est souvent la raison pour laquelle même ceux qui connaissent et aiment cette technique remettent à plus tard leur création.

Et plus tard, c’est en général trop tard. Au moment où on commence à souffrir de leur manque il faut repasser sur tout un tas de tests pour les mettre en place...

Mais les Live Templates peuvent nous aider à mettre en place nos builders super rapidement, si rapidement que la question de créer ou ne pas créer un builder ne se pose plus.

Tu en as marre de tes tests ?

J’ai créé une formation vidéo qui aide les développeuses et développeurs à améliorer leurs tests automatisés.

Dans cette formation je partage les idées et techniques qui permettent de rendre des tests lents, qui cassent à chaque modification du code et sont incompréhensibles en des tests avec lesquels on a plaisir à travailler.

Laissez-moi vous présenter la classe Burrito, qui va nous servir de support d’exemple pour la suite.

<?php

final class Burrito
{
    public function __construct(
        private readonly string $burritoName,
        private readonly Salsa $salsa,
        private readonly array $ingredients
    )
    {}
}

Le constructeur de cette classe à 3 paramètres :

  • $burritoName de type string
  • $salsa de type Salsa
  • $ingredients de type array

Création d’une méthode de paramétrage du builder

Dans la classe BurritoBuilder il faut ajouter des méthodes qui vont nous permettre de paramétrer la construction du Burrito.

Un gif qui fait la démonstration du live template "with". La description est ci-dessous.

Dans l’exemple on ajoute deux méthodes de paramétrage en quelques secondes. L’une pour la sauce et l’autre pour le nom du burrito. Ce Live Template fonctionne aussi bien pour un type natif que pour une classe que l’on a créée.

Voilà ce qui se passe :

  • Je tape "with"
  • Tabulation
  • J’indique le type du paramètre. Salsa ou string ici.
  • Tabulation
  • Je dois indiquer le nom du paramètre. Ici l’autocomplete m’aide. S’il s’agit d’une classe il propose directement le nom de la classe comme nom de variable. $salsa par exemple.
  • Le nom me convient, Entrée. Le nom me convient pas, je le change.
  • Le nom de la propriété à laquelle est assignée la variable prend directement le nom que je viens de choisir
  • Tabulation
  • Le curseur se place au niveau de l’assignation de la propriété
  • Alt + Entrée, le raccourci à tout faire de l’IDE.
  • Choisir d’ajouter la propriété manquante
  • Entrée

Et voilà. La propriété est créée, la méthode pour lui assigner une valeur également.

<template name="with" value="public function with$NAME$($PARAMETER_TYPE$ $$$PARAMETER_NAME$): self&#10;{&#10;&#9;$clone = clone $this;&#10;&#9;&#10;&#9;$clone->$PARAMETER_NAME$$END$ = $$$PARAMETER_NAME$;&#10;&#9;&#10;&#9;return $clone;&#10;}" description="Create a wither" toReformat="false" toShortenFQNames="true">
  <variable name="PARAMETER_TYPE" expression="" defaultValue="" alwaysStopAt="true" />
  <variable name="PARAMETER_NAME" expression="complete()" defaultValue="" alwaysStopAt="true" />
  <variable name="NAME" expression="" defaultValue="capitalize(PARAMETER_NAME)" alwaysStopAt="false" />
  <context>
    <option name="PHP Class Member" value="true" />
  </context>
</template>

Mais ce n’est pas fini.

Création de la méthode build

Qui dit builder dit méthode build. Toutes les méthodes build se ressemblent. Au moins au début.

On construit un objet en lui passant en paramètre les propriétés du builder et on le retourne.

Il est également possible d’utiliser un Live Template pour là aussi gagner du temps. En réalité il s’agit même de deux Live Templates qui se combinent bien ensemble.

Un gif qui fait la démonstration du live template "with". La description est ci-dessous.

Le premier Live Template, build, créer la méthode build, place le curseur au niveau du type de retour, l’autocomplétion est lancée et nous aide à choisir le type.

Automatiquement le constructeur du type est appelé avec le mot clé new.

On fait une tabulation et le curseur se place dans la liste des paramètres. L’autocomplétion est démarrée et nous propose la bonne propriété à utiliser pour le premier paramètre.

C’est là que le second LT entre jeu. Le constructeur de Burrito prend plusieurs paramètres et il est possible de les ajouter très rapidement.

Ce second Live Template est bindé sur ,.

Oui, oui, la virgule.

, + Tabulation démarre automatiquement l’autocomplétion, qui se charge de proposer la bonne propriété pour le paramètre.[4]

Le snippet du Live Template build:

<template name="build" value="public function build(): $TYPE$&#10;{&#10;    return new $TYPE$($PARAMETERS$$END$);&#10;}" description="Build method" toReformat="false" toShortenFQNames="true">
  <variable name="TYPE" expression="complete()" defaultValue="" alwaysStopAt="true" />
  <variable name="PARAMETERS" expression="completeSmart()" defaultValue="" alwaysStopAt="true" />
  <context>
    <option name="PHP Class Member" value="true" />
  </context>
</template>

et celui de ,:

<template name="," value=", $COMPLETE$" description=", and auto complete" toReformat="false" toShortenFQNames="true">
  <variable name="COMPLETE" expression="completeSmart()" defaultValue="" alwaysStopAt="true" />
  <context>
    <option name="PHP Expression" value="true" />
    <option name="PHP Statement" value="true" />
  </context>
</template>

Conclusion

J’espère que ces quelques exemples d’utilisation des Live Templates vous auront donné envie de créer les vôtres. Si vous cherchez de l’inspiration je vous invite à lire cet article de Marijn Huizendveld[5] dans lequel il partage ces Live Templates pour créer des Command Handlers, des Values Objects, des collections...

Devenez fainéants !

Et si vous pensez qu'un accompagnement technique pourrait être bénéfique pour votre équipe, rencontrons-nous et trouvons ensemble comment vous faire passer au niveau supérieur.

Si vous voulez prêcher la bonne parole de la fainéantise auprès de vos amis cet article est aussi disponible sous forme de thread Twitter[6].


  1. Ça doit avoisiner 4 doigts sur les deux mains. ↩︎

  2. Il est possible de faire tourner des scripts externes, mais je n’ai pas encore eu l’occasion de tester. J’ai quelques idées cela dit. ↩︎

  3. Je tiens cette idée de Lazy Naming de Romeu Moura qu’il présente dans cette conférence ↩︎

  4. Le Live Template bindé sur la virgule est une nouvelle idée et est en cours d’expérimentation. Je ne serais pas étonné d’avoir des mauvaises surprises un de ces jours 😅. Vous êtes prévenus. ↩︎

  5. C’est cet article qui m’a fait connaitre la méthode facile de partage des Live Templates. Merci Marijn. ↩︎

  6. Tant que Twitter existe, tout du moins. ↩︎