Question

Dernièrement, j'ai dû modifier du code sur des systèmes plus anciens où tout le code n'avait pas de tests unitaires.
Avant d'apporter les modifications, je souhaite écrire des tests, mais chaque classe a créé de nombreuses dépendances et autres anti-modèles, ce qui a rendu les tests assez difficiles.
Évidemment, je voulais refactoriser le code pour faciliter les tests, écrire les tests puis le modifier.
Est-ce ainsi que vous procéderiez ?Ou passeriez-vous beaucoup de temps à écrire les tests difficiles à écrire qui seraient pour la plupart supprimés une fois la refactorisation terminée ?

Était-ce utile?

La solution

Tout d'abord, voici un excellent article avec des conseils sur les tests unitaires.Deuxièmement, j'ai trouvé un excellent moyen d'éviter d'apporter des tonnes de modifications à l'ancien code : il suffit de le refactoriser un peu jusqu'à ce que vous puissiez le tester.Un moyen simple de procéder consiste à protéger les membres privés, puis à remplacer le champ protégé.

Par exemple, disons que vous avez une classe qui charge des éléments de la base de données pendant le constructeur.Dans ce cas, vous ne pouvez pas simplement remplacer une méthode protégée, mais vous pouvez extraire la logique de la base de données dans un champ protégé, puis la remplacer dans le test.

public class MyClass {
    public MyClass() {
        // undesirable DB logic
    }
}

devient

public class MyClass {
    public MyClass() {
        loadFromDB();
    }

    protected void loadFromDB() {
        // undesirable DB logic
    }
}

et puis votre test ressemble à ceci :

public class MyClassTest {
    public void testSomething() {
        MyClass myClass = new MyClassWrapper();
        // test it
    }

    private static class MyClassWrapper extends MyClass {
        @Override
        protected void loadFromDB() {
            // some mock logic
        }
    }
}

C'est un mauvais exemple, car vous pourriez utiliser DBUnit dans ce cas, mais je l'ai fait récemment dans un cas similaire parce que je voulais tester certaines fonctionnalités totalement indépendantes des données en cours de chargement, donc c'était très efficace.J'ai également trouvé qu'une telle exposition des membres était utile dans d'autres cas similaires où je devais me débarrasser d'une dépendance qui existe depuis longtemps dans une classe.

Je déconseille cette solution si vous écrivez un framework, à moins que cela ne vous dérange vraiment pas d'exposer les membres aux utilisateurs de votre framework.

C'est un peu un hack, mais je l'ai trouvé très utile.

Autres conseils

@valters

Je ne suis pas d'accord avec votre affirmation selon laquelle les tests ne devraient pas interrompre la construction.Les tests doivent indiquer que l'application n'a pas introduit de nouveaux bogues pour la fonctionnalité testée (et un bogue trouvé est une indication d'un test manquant).

Si les tests n'interrompent pas la construction, vous pouvez facilement vous retrouver dans une situation où un nouveau code interrompt la construction et n'est pas connu pendant un certain temps, même si un test l'a couvert.Un test échoué doit être un signal d’alarme indiquant que le test ou le code doit être corrigé.

De plus, permettre aux tests de ne pas interrompre la construction entraînera une augmentation lente du taux d'échec, au point que vous ne disposerez plus d'un ensemble fiable de tests de régression.

S'il y a un problème avec des tests qui se cassent trop souvent, cela peut être le signe que les tests sont écrits de manière trop fragile (dépendance à des ressources qui pourraient évoluer, comme la base de données sans utiliser correctement DB Unit, ou un service web externe cela devrait être ridiculisé), ou cela peut être une indication qu'il y a des développeurs dans l'équipe qui n'accordent pas l'attention voulue aux tests.

Je crois fermement qu'un test qui échoue doit être corrigé dès que possible, tout comme vous corrigeriez un code qui ne parvient pas à se compiler dès que possible.

Je ne sais pas pourquoi diriez-vous que les tests unitaires seront supprimés une fois la refactorisation terminée.En fait, votre suite de tests unitaires devrait s'exécuter après la construction principale (vous pouvez créer une version "tests" distincte, qui exécute simplement les tests unitaires après la construction du produit principal).Ensuite, vous verrez immédiatement si les modifications apportées dans un seul élément interrompent les tests dans l'autre sous-système.Notez que c'est un peu différent de l'exécution de tests pendant la construction (comme certains peuvent le préconiser) - certains tests limités sont utiles pendant la construction, mais il est généralement improductif de "planter" la construction simplement parce qu'un test unitaire échoue.

Si vous écrivez Java (il y a de fortes chances), consultez http://www.easymock.org/ - peut être utile pour réduire le couplage à des fins de test.

J'ai lu Travailler efficacement avec le code hérité et je reconnais que c'est très utile pour traiter du code "non testable".

Certaines techniques ne s'appliquent qu'aux langages compilés (je travaille sur des "anciennes" applications PHP), mais je dirais que la majeure partie du livre est applicable à n'importe quel langage.

Les livres de refactoring supposent parfois que le code est dans un état semi-idéal ou « conscient de la maintenance » avant la refactorisation, mais les systèmes sur lesquels je travaille sont loin d'être idéaux et ont été développés comme des applications « d'apprentissage au fur et à mesure », ou comme premières applications pour certaines technologies utilisées. (et je ne blâme pas les développeurs initiaux pour cela, puisque j'en fais partie), donc il n'y a aucun test du tout et le code est parfois compliqué.Ce livre aborde ce genre de situation, alors que d'autres livres de refactoring ne le font généralement pas (enfin, pas dans cette mesure).

Je dois préciser que je n'ai reçu aucune somme de l'éditeur ni de l'auteur de ce livre ;), mais je l'ai trouvé très intéressant, car les ressources manquent dans le domaine du code existant (et particulièrement dans ma langue, le français, mais c'est une autre histoire).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top