Oui, SOLID est un très bon moyen de concevoir du code facilement testable. En quelques mots:
S - Principe de responsabilité unique: un objet doit faire exactement une chose et doit être le seul objet de la base de code qui le fait. Par exemple, prenons une classe de domaine, disons une facture. La classe Invoice doit représenter la structure de données et les règles de gestion d'une facture, telles qu'elles sont utilisées dans le système. Ce devrait être la seule classe qui représente une facture dans la base de code. Cela peut encore être décomposé pour dire qu'une méthode devrait avoir un but et devrait être la seule méthode de la base de code qui répond à ce besoin.
En suivant ce principe, vous augmentez la testabilité de votre conception en diminuant le nombre de tests que vous devez écrire pour tester la même fonctionnalité sur différents objets. Vous obtenez également généralement de plus petites fonctionnalités qui sont plus faciles à tester isolément.
O - Principe ouvert / fermé: une classe doit être ouverte à l’extension, mais fermée au changement . Une fois qu'un objet existe et fonctionne correctement, idéalement, il ne devrait pas être nécessaire d'y revenir pour apporter des modifications qui ajoutent de nouvelles fonctionnalités. Au lieu de cela, l'objet doit être étendu, soit en le dérivant, soit en y insérant des implémentations de dépendance nouvelles ou différentes, afin de fournir cette nouvelle fonctionnalité. Cela évite la régression; vous pouvez introduire la nouvelle fonctionnalité quand et où elle est nécessaire, sans changer le comportement de l'objet tel qu'il est déjà utilisé ailleurs.
En adhérant à ce principe, vous augmentez généralement la capacité du code à tolérer les "simulacres", et vous évitez également de devoir réécrire des tests pour anticiper un nouveau comportement. tous les tests existants pour un objet doivent toujours fonctionner sur l'implémentation non étendue, tandis que les nouveaux tests pour les nouvelles fonctionnalités utilisant l'implémentation étendue doivent également fonctionner.
L - Principe de substitution de Liskov: Une classe A, dépendant de la classe B, devrait pouvoir utiliser n’importe quel X: B sans connaître la différence. Cela signifie fondamentalement que tout ce que vous utilisez comme dépendance devrait avoir un comportement similaire à celui observé par la classe dépendante. Par exemple, supposons que votre interface IWriter expose Write (chaîne), qui est implémentée par ConsoleWriter. Maintenant, vous devez écrire dans un fichier, vous créez donc FileWriter. Pour ce faire, vous devez vous assurer que FileWriter peut être utilisé de la même manière que ConsoleWriter (ce qui signifie que la seule façon pour la personne dépendante d'interagir avec elle est d'appeler Write (chaîne)), et donc des informations supplémentaires dont FileWriter peut avoir besoin pour ce faire. Le travail (comme le chemin et le fichier dans lequel écrire) doit être fourni ailleurs que par la personne à charge.
C'est énorme pour l'écriture de code testable, car une conception conforme au LSP peut avoir un objet "simulé" à la place de la chose réelle à tout moment sans changer le comportement attendu, permettant ainsi de tester de petits morceaux de code de manière isolée avec la confiance. que le système fonctionnera ensuite avec les objets réels branchés.
I - Principe de séparation des interfaces: une interface doit avoir le moins de méthodes possible pour fournir les fonctionnalités du rôle défini par l'interface . En termes simples, plus d'interfaces plus petites sont meilleures que moins d'interfaces plus grandes. En effet, une grande interface a plus de raisons de changer et provoque plus de modifications ailleurs dans la base de code qui peuvent ne pas être nécessaires.
L'adhésion à l'ISP améliore la testabilité en réduisant la complexité des systèmes testés et des dépendances de ces unités sous test. Si l'objet que vous testez dépend d'une interface IDoThreeThings qui expose DoOne (), DoTwo () et DoThree (), vous devez simuler un objet qui implémente les trois méthodes, même s'il utilise uniquement la méthode DoTwo. Cependant, si l'objet ne dépend que de IDoTwo (qui n'expose que DoTwo), vous pouvez plus facilement simuler un objet qui possède cette méthode.
D - Principe de l'inversion de dépendance: Les concrétions et les abstractions ne doivent jamais dépendre d'autres concrétions, mais bien des abstractions . Ce principe applique directement le principe du couplage lâche. Un objet ne devrait jamais avoir à savoir ce qu'est un objet. il devrait plutôt se préoccuper de ce que fait un objet. Ainsi, l'utilisation d'interfaces et / ou de classes de base abstraites doit toujours être préférée à l'utilisation d'implémentations concrètes lors de la définition des propriétés et des paramètres d'un objet ou d'une méthode. Cela vous permet d’échanger une implémentation pour une autre sans avoir à changer l’utilisation (si vous suivez également LSP, qui va de pair avec DIP).
Encore une fois, c’est énorme pour la testabilité, car cela vous permet, encore une fois, d’injecter une implémentation fictive d’une dépendance plutôt que celle de "production" dans votre objet en cours de test, tout en continuant de tester l’objet sous la forme exacte en production. C'est la clé du test unitaire "en vase clos".