Modèles source de tests unitaires

10

J'ai plusieurs modèles dans mon extension personnalisée qui ne servent qu'à remplir des sélections et / ou des multi-sélections dans le formulaire d'ajout / modification de mes entités.
Ils sont donc ce que magento appelle des "modèles source".
Les valeurs impliquées sont toujours les mêmes et les méthodes renvoient la même chose.
Comment dois-je les tester à l'unité? Ou encore mieux, dois-je leur faire des tests unitaires?
Voici un exemple.
La classe suivante est utilisée pour ajouter / modifier un formulaire pour un champ appelé typeet pour la colonne de grille du même champ.

<?php
namespace Sample\News\Model\Author\Source;

use Magento\Framework\Option\ArrayInterface;

class Type implements ArrayInterface
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;

    /**
     * Get options
     *
     * @return array
     */
    public function toOptionArray()
    {
        $_options = [
            [
                'value' => '',
                'label' => ''
            ],
            [
                'value' => self::COLLABORATOR,
                'label' => __('Collaborator')
            ],
            [
                'value' => self::EMPLOYEE,
                'label' => __('Employee')
            ],
        ];
        return $_options;
    }

    /**
     * get options as key value pair
     *
     * @return array
     */
    public function getOptions()
    {
        $_tmpOptions = $this->toOptionArray();
        $_options = [];
        foreach ($_tmpOptions as $option) {
            $_options[$option['value']] = $option['label'];
        }
        return $_options;
    }
}
Marius
la source

Réponses:

15

C'est un excellent fil de discussion et j'aime beaucoup les réponses de @KAndy et @fschmengler.
Je voudrais ajouter quelques réflexions supplémentaires que je trouve précieuses lorsque je pose une question telle que "Dois-je tester X?" ou "Comment dois-je tester X?".

Qu'est-ce qui pourrait mal se passer?

  • Je pourrais faire une faute de frappe (cela arrive tout le temps)
    Cela ne justifie généralement pas la rédaction d'un test.
  • Vais-je copier le code dont j'ai besoin à partir du noyau ou d'un module différent, puis l'adapter à mes besoins?
    Je trouve que c'est en fait une chose très dangereuse à faire qui laisse souvent des bugs subtils. Dans ce cas, je préfère écrire un test, s'il n'est pas trop cher. Faire la configuration des modèles source en fait les rendrait plus risqués OMI.
  • Peut-il y avoir un conflit avec un module différent?
    Cela s'applique presque uniquement au code de configuration. Dans un tel cas, j'aime avoir un test d'intégration qui me dit quand cela se produit.
  • Magento pourrait-il changer l'API dans une future version?
    Très peu probable dans ce cas, car votre code ne dépend que d'une interface. Mais les classes les plus concrètes sont impliquées ou si mon code étend une classe de base, cela devient plus un risque potentiel.
  • Une nouvelle version PHP pourrait casser mon code. Ou peut-être que je veux prendre en charge PHP 5.6 pour les années à venir.
    Encore une fois, très peu probable ici, mais dans certains cas, je veux qu'un test m'avertisse, si je change le code à l'avenir pour utiliser une syntaxe incompatible.

Combien coûte le test du code?

Cela comporte deux aspects:

  • La quantité d'efforts et de temps nécessaires pour écrire un test
  • La quantité d'efforts et de temps nécessaires pour tester le morceau de code que je suis sur le point d'écrire manuellement.

Pendant le développement d'un morceau de code, j'ai tendance à devoir exécuter le code que j'écris assez souvent jusqu'à ce que je le considère comme terminé. C'est bien sûr beaucoup plus facile avec un test unitaire.

Dans votre cas, écrire un test est vraiment bon marché. Cela ne prend pas beaucoup de temps ou d'efforts. @KAndy a raison de dire que tout le code doit être maintenu, mais là encore, tous les tests ne doivent pas être conservés.
Cela pourrait être un exemple où j'écrirais un test unitaire, juste pour vérifier que je ne fais pas une erreur stupide, puis le supprimer une fois la classe terminée. Si un test ne fournit pas de valeur à long terme, je pense que les supprimer est logique.

Cette question est également importante pour le choix du type de test à écrire: unité ou intégration par exemple.

Quelle est la valeur du morceau de code que j'écris?

Si un morceau de code que j'écris est essentiel pour le service fourni par un module, je le teste, quelle que soit sa banalité.
Si c'est juste une petite méthode d'utilité, par exemple axée sur l'interface utilisateur, et ne fait pas partie de la logique métier, alors peut-être pas.

Le code devra-t-il changer?

Au fil du temps, je suis devenu tellement habitué à avoir une couverture de test, que le changement de code découvert ne semble pas très sûr. Cela inclut des choses si simples comme l'ajout d'une option à un modèle source, mais aussi des choses comme le déplacement d'une classe vers un dossier / espace de noms différent, ou le changement de nom d'une méthode.
La mise en place de tests pour de tels changements est inestimable.

A-t-il besoin de documentation?

Est-il difficile d'utiliser le code? Dans votre exemple, c'est trivial, mais dans certains cas plus complexes, avoir un test est idéal à des fins de documentation pour d'autres développeurs (ou moi-même dans quelques mois).

Exploration et apprentissage

Si je travaille sur du code et que je ne sais pas comment le tester, je trouve très utile d'écrire un test. Le processus me donne presque toujours une meilleure compréhension de ce à quoi je fais face.
Cela est particulièrement vrai pour les développeurs qui se considèrent toujours en train d'apprendre à tester.
C'est également un exemple où il pourrait être judicieux de supprimer le test par la suite, car la principale valeur qu'il a fournie était l'apprentissage.

Discipline et stress

Coller à la boucle refactor rouge-vert m'aide à aller vite. Cela est particulièrement vrai sous pression. Donc, même si un morceau de code n'est pas vraiment digne d'un test, je pourrais toujours suivre TDD, surtout si le code est trivial à tester.
Cela me maintient dans le flux et l'alerte.

Quoi et comment tester?

Considérez également que vous pouvez écrire le test dans une granularité très différente.

  • Test de la valeur de retour exacte.
    Ce sera un test très rigide qui devra être ajusté à chaque changement. Voulez-vous que le test se casse, par exemple, si l'ordre des éléments dans le tableau de retour change?
  • Test de la structure de la valeur de retour.
    Pour le modèle source, cela pourrait consister à vérifier chaque sous-tableau comme deux enregistrements, un avec un labelet un avec une valueclé.
  • Vérification des implémentations de classe ArrayInterface.
  • Tester la classe fournit getOptions()même si cette méthode ne fait pas partie de l'interface implémentée.

Pour chaque chose possible qui peut être testée, considérez la valeur, la maintenabilité et le coût.

Résumé

Pour résumer: il n'y a pas de vraie réponse unique à une question si un morceau de code doit être testé ou non. La réponse sera différente pour chaque développeur selon les circonstances.

Vinai
la source
2
Très bonne réponse! Je veux mettre en évidence la partie "supprimer les tests quand ils ne fournissent plus de valeur" - parfois les vieux tests qui ont aidé pendant le développement initial, ne font que gêner le long terme.
Fabian Schmengler
1
vous venez de répondre à 2 autres questions dont je doutais. merci
Marius
6

À mon avis, il n'y a pas de réponse générale à «écrire des tests unitaires pour les modèles source, oui ou non»

J'ai écrit des tests unitaires pour les modèles source, mais il s'agissait de modèles source dynamiques qui récupéraient des données externes et là, cela a un sens total.

Pour les modèles source qui ne sont rien d'autre que des tableaux glorifiés (comme dans votre exemple), je ne prendrais pas la peine d'écrire des tests unitaires. Mais d'une certaine manière, vous devez vous assurer que vous n'avez pas fait d'erreur. Il existe plusieurs options:

  1. Test de l'unité
  2. test d'intégration
  3. regarder manuellement la configuration

Suivez-vous TDD? Choisissez ensuite entre (1) et (2), ou les deux. Sinon, choisissez entre (2) et (3).


Si vous utilisez les modèles source pour les options de configuration du système, un test d'intégration (un test pour toutes vos options de configuration) pourrait rendre la section de configuration et vérifier si les <select>éléments sont présents et contiennent les valeurs attendues. N'oubliez pas qu'il ne s'agit pas de tester une classe particulière mais de vérifier que tout est correctement lié.


Et comme @KAndy l'a dit, l'idéal serait que vous n'ayez pas besoin de beaucoup de passe-partout, il suffit d'étendre une classe de base qui est déjà testée à l'unité et de remplacer une propriété ou de définir les valeurs dans une configuration externe.

Dans ce scénario, les tests unitaires pour les implémentations concrètes n'apportent aucune valeur supplémentaire. Si vous avez plusieurs de ces classes, ce pourrait être une bonne idée d'écrire une classe de base ArraySourcevous - même, tant que Magento ne la fournit pas.


Avec une telle classe de base, votre modèle source pourrait ressembler à ceci:

class Type extends ArraySource
{
    const COLLABORATOR = 1;
    const EMPLOYEE = 2;
    protected $elements = [
        self::COLLABORATOR => 'Collaborator',
        self::EMPLOYEE     => 'Employee',
    ];
    protected $withEmpty = true;
    protected $translate = true;
}
Fabian Schmengler
la source
Merçi pour la confirmation. J'essaierai de transformer mes tableaux glorifiés en une liste d'options configurable, mais est-ce la même chose pour les modèles qui auront exactement N options? Comme un modèle source "d'état".
Marius
Je ne vois pas en quoi un modèle source de "statut" serait différent de votre exemple de "type d'auteur"
Fabian Schmengler
car je pourrais utiliser les valeurs 0 et 1 (comme constantes de classe) sur le frontend, pour filtrer par exemple les entités activées. Je veux dire dans ce cas, les valeurs des options ont une logique derrière elles. ce ne sont pas seulement des key=>valuepaires.
Marius
Mais cette logique ne fait pas partie du modèle source, non? Cela signifie que cela n'a pas d'importance ici. Vous aurez toujours les constantes à utiliser dans d'autres endroits. J'ai ajouté un exemple de la façon dont j'utiliserais une telle implication.
Fabian Schmengler
5

Comment dois-je les tester à l'unité?

Je pense que tu ne devrais pas.

Ajoutez du code au système pour augmenter les coûts de support et de maintenance, mais le processus de test doit être LEAN .

Plus sur ce code ne devrait pas exister. Je crois que dans Magento devrait être une façon déclarative générique de définir des ensembles d'options à utiliser à différents endroits. Et votre réticence à écrire un test pour cette classe me montre une odeur de mauvais code

KAndy
la source
1
Merci. Vous dites donc que ces options ne doivent pas être codées en dur mais provenir d'un fichier de configuration (di.xml par exemple). Je peux le faire. Mais en est-il de même pour les modèles de source de statut? Je veux dire, si j'ai la même classe que ci-dessus mais uniquement avec le statut activé et désactivé (1,0) dois-je utiliser la même approche de configuration? Si oui, je veux dire que cela me semble comme une ingénierie excessive.
Marius