L'injection de dépendance est-elle essentielle pour les tests unitaires?

56

L'utilisation de l'injection de dépendance (ID) est-elle essentielle pour les tests unitaires?

Je ne peux pas penser à une autre alternative pour isoler le code afin qu'il puisse être testé. En outre, tous les exemples que j'ai vus utilisent ce modèle. Est-ce parce que c'est la seule option viable ou existe-t-il d'autres alternatives?

Tom Squires
la source
5
L'injection de dépendance n'est pas essentielle, mais le concept plus large d'Inversion of Control l'est.
Jeremy Heiler
Il y a quelque chose à dire pour l'échelle ici. Si j'ai une petite base de code avec très peu de couches, alors DI peut ne pas être utile.
JB King
@JBKing Si vous avez une petite base de code, vous n'avez pas besoin de couches ni de tests unitaires
Sklivvz
1
De nombreuses décisions de conception sont nécessaires pour rendre votre code testable. Commencez à écrire des tests et découvrez.
Nathan Cooper

Réponses:

43

DI facilite grandement les tests unitaires. Mais vous pouvez toujours écrire des tests unitaires sans DI. De nombreux tests unitaires ont déjà été écrits avant la généralisation de l'ID. (Bien sûr, certaines de ces techniques utilisées sont identiques ou très similaires à DI sans savoir qu’elle a un nom compliqué :-)

J'ai moi-même beaucoup utilisé, par exemple, les interfaces et les usines avant de connaître le DI. Le nom actuel de la classe de fabrique peut avoir été lu dans un fichier de configuration ou transmis au SUT en tant qu'argument.

Une autre approche consiste à utiliser des singletons (ou des données globalement accessibles). Oui, je sais que ce n'est pas recommandé par beaucoup (y compris moi-même) en général. Néanmoins, il peut être viable dans des situations spécifiques, notamment si le singleton contient des données de configuration statiques qui ne sont pas spécifiques à un cas de test, mais diffèrent entre l'environnement de production et l'environnement de test. Bien sûr, il a ses problèmes connus, donc DI est supérieur si vous pouvez l'utiliser. Mais souvent (par exemple, dans les systèmes existants), vous ne pouvez pas.

Parler qui, Travailler efficacement avec Legacy Code décrit beaucoup de trucs pour obtenir le code existant couvert par des tests. Beaucoup d'entre eux ne sont pas agréables et ne constituent pas une solution à long terme. Mais ils vous permettent de créer les premiers tests unitaires de valeur sur un système par ailleurs indéfectible ... ce qui vous permet de commencer la refactorisation et, éventuellement, d’introduire DI.

Péter Török
la source
D'accord, DI est particulièrement utile pour se moquer d'objets. Mais il existe de nombreux scénarios dans lesquels l’ID n’est pas utile dans les tests.
Kemoda
@Kemoda comme par exemple quoi?
B12овић
@VJovic Par exemple, les objets autonomes. Je pense à une méthode qui prend des arguments et effectue des tâches mais ne dépend pas d'un autre composant (donc pas besoin de DI).
Kemoda
@Kemoda On dirait que vous décrivez la programmation fonctionnelle et que vous utilisez DI. Vous injectez vos dépendances en tant que paramètres de méthode.
Erik Dietrich
3
@ huggie, pourquoi les détails de la mise en œuvre seraient-ils divulgués ici? La dépendance injectée est généralement cachée derrière une interface et tout le problème est que la classe client n'a aucune idée de - et ne s'intéresse pas à - si l'implémentation réelle de cette dépendance est une vraie classe de production ou une maquette. Son instanciation se produit en dehors de la classe client, il ne peut voir que l'instance prête à l'emploi.
Péter Török
56

Le découplage est essentiel pour les tests unitaires. DI est un excellent moyen de réaliser le découplage.

nlawalker
la source
17
Une déclaration très vraie qui ne répond en aucune façon à la question.
Travis
10

Selon les technologies que vous utilisez, vous pouvez isoler des dépendances sans utiliser DI. Par exemple, dans le monde .NET, Moles vous permet d'isoler des dépendances sans le motif DI.

Cela dit, je pense que ces frameworks d’isolation sont écrits et destinés à des situations de code comportant des dépendances externes (système de fichiers, base de données, etc.). C'est-à-dire que le fait de pouvoir faire cela ne veut pas dire qu'il le devrait.

L'injection de dépendance permet les tests unitaires, mais elle permet également de modifier le comportement d'un objet sans modifier le code de cet objet (principe ouvert / fermé). Ainsi, il ne s'agit pas uniquement d'un code testable, mais d'un code flexible. J'ai généralement constaté qu'il existe une forte corrélation entre le code maintenable / flexible et le code testable.

Erik Dietrich
la source
3
Ou Moles vous laisse penser que vous avez un code testable, propre et bien factorisé, parce que vous testez par magie.
Wyatt Barnett
@WyattBarnett Ouais, très vrai. Moles est capable de faire dire à quelqu'un "qui a besoin de tout ce polymorphisme et de tout ce qui est ouvert / fermé, de toute façon?!?"
Erik Dietrich
1
Les taupes ont été remplacées par des faux , et à partir de la page des faux "Le framework Fakes aide les développeurs à créer, maintenir et injecter des implémentations factices dans leurs tests unitaires" Cela me ressemble.
Inscrivez
1
@Sign Votre lien indique également: "Le framework Fakes peut être utilisé pour shimner n'importe quelle méthode .NET, y compris les méthodes non virtuelles et statiques dans des types scellés.". Le fait que les cadres d'isolation puissent être utilisés conjointement avec DI ne signifie pas qu'ils sont DI.
Erik Dietrich
4

Non, l'ID n'est pas indispensable pour les tests unitaires, mais cela aide beaucoup.

Vous pouvez utiliser des usines ou des localisateurs et tester comme vous le feriez avec DI (mais pas aussi élégant et nécessiterait plus de configuration).

En outre, les objets fantaisie seraient importants dans les systèmes existants, dans lesquels de nombreux appels sont délégués à des fonctions plutôt qu'à des dépendances. (Les objets fantaisie peuvent également être largement utilisés dans une configuration appropriée)

Il peut y avoir des configurations où les tests sont presque impossibles. Mais cela ne dépend pas de l'utilisation ou non de l'injection de dépendance.

smp7d
la source
3

Non , l'injection de dépendance n'est pas indispensable pour les tests unitaires.

L'injection de dépendance est utile si vous avez une classe qui a besoin d'une instance de classe dépendante pour effectuer un sous-traitement. Au lieu de DI, vous pouvez séparer la logique d'une méthode métier en une partie de collecte de données (qui n'est pas testable par unité) et une partie de calcul qui peut être testée en unité.

Exemple (utilisation de DI) Cette implémentation dépend de Employee, Account, ...

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     if (amount > 100 && employee.isStudent())
        return false;
     if (to.getOwner().getFamiliyName() == employee.getFamilyName() && ...
        return false; // cannot transfer money to himself;
     ...
 }

Après séparation de la collecte et du calcul des données:

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     return hasPermissionToTransferMoney(employee.isStudent(), employee.getFamilyName(), to.getOwner().getFamilyName(), ...);
 }

 // the actual permission calculation
 static bool hasPermissionToTransferMoney(boolean isStudent, string employeeFamilyName, string receiverFamilyName, ...)
     if (amount > 100 && isStudent)
        return false;
     if (receiverFamilyName == employeeFamiliyName && ...
        return false; // cannot transfer money to himself
     ...
 }

La partie calcul peut être facilement testée sans injection de dépendance.

k3b
la source
1
  • L'injection de dépendance n'est pas essentielle pour les tests unitaires

  • L'inversion du contrôle est en revanche essentielle lorsque vous souhaitez échanger une mise en œuvre contre une autre.

CodeART
la source
0

Oui, il existe des alternatives à l'utilisation de DI pour l'isolement.

Une alternative consiste à utiliser des fabriques ou un ServiceLocator pouvant être configuré à partir de tests pour renvoyer des objets fictifs au lieu d'objets réels.

Une autre consiste à utiliser un cadre d'isolation approprié ou un outil moqueur. De tels outils existent pour à peu près tous les langages de programmation modernes (pour Java, C #, Ruby et Python, au moins). Ils peuvent isoler une classe en cours de test de la mise en œuvre d'autres classes / types, même lorsque la classe testée instancie directement ses dépendances.

Rogério
la source