web-dev-qa-db-fra.com

Mettez-vous des tests unitaires dans le même projet ou dans un autre projet?

Mettez-vous des tests unitaires dans le même projet pour plus de commodité ou les mettez-vous dans une assemblée séparée?

Si vous les mettez dans une assemblée séparée comme nous, nous nous retrouvons avec un certain nombre de projets supplémentaires dans la solution. C'est génial pour les tests unitaires lors du codage, mais comment libérer l'application sans tous ces assemblages supplémentaires?

126
leora

À mon avis, les tests unitaires devraient être placés dans une assemblée distincte du code de production. Voici quelques inconvénients de placer des tests unitaires dans le même ou les mêmes assemblages que le code de production:

  1. Les tests unitaires sont livrés avec le code de production. La seule chose livrée avec le code produit est le code de production.
  2. Les assemblages seront gonflés inutilement par des tests unitaires.
  3. Les tests unitaires peuvent affecter les processus de construction tels que la construction automatisée ou continue. 

Je ne connais pas vraiment de pros. Avoir un projet supplémentaire (ou 10) n'est pas un inconvénient.

Modifier: Plus d'infos sur la construction et l'expédition

Je recommanderais en outre que tout processus de construction automatisé confie la production et les tests unitaires à des emplacements différents. Idéalement, le processus de création de tests unitaires ne s'exécute que si le code de production est généré et copie les fichiers du produit dans le répertoire de tests unitaires. En procédant de cette façon, les bits réels sont séparés pour l'expédition, etc. De plus, il est assez simple d'exécuter des tests unitaires automatisés à ce stade sur tous les tests d'un répertoire particulier.

En résumé, voici l’idée générale qui consiste à créer, tester et expédier quotidiennement des bits et d’autres fichiers:

  1. La génération de production s’exécute en plaçant les fichiers de production dans un répertoire "de production" spécifique .
    1. Construire des projets de production seulement.
    2. Copiez les bits compilés et d’autres fichiers dans un répertoire "de production".
    3. Copiez des bits et d’autres fichiers dans un répertoire de versions, un répertoire de version de Noël serait "Release20081225".
  2. Si la production est réussie, la construction du test unitaire est exécutée .
    1. Copiez le code de production dans le répertoire "tests".
    2. Construire des tests unitaires dans le répertoire "tests".
    3. Exécutez des tests unitaires.
  3. Envoyez les notifications de construction et les résultats des tests unitaires aux développeurs.
  4. Quand une version candidate (telle que la Release20081225) est acceptée, expédiez ces bits.
88
Jason Jackson

Projet séparé, mais dans la même solution. (J'ai travaillé sur des produits avec des solutions séparées pour le code de test et de production - c'est horrible. Vous basculez toujours entre les deux.)

Les raisons pour des projets séparés sont comme indiqué par d'autres. Notez que si vous utilisez des tests pilotés par les données, vous risquez de vous retrouver avec une quantité importante de gonflement si vous incluez les tests dans l'assemblage de production.

Si vous avez besoin d'accéder aux membres internes du code de production, utilisez InternalsVisibleTo .

98
Jon Skeet

Je ne comprends pas l’objection fréquente au déploiement de tests avec le code de production. J'ai dirigé une équipe dans une petite microcap (passant de 14 à 130 personnes). Nous avions une demi-douzaine d'applications Java et nous avons trouvé extrêmement intéressant de déployer des tests sur le terrain pour les exécuter sur une machine {spécifique} présentant un comportement inhabituel. Des problèmes aléatoires se produisent sur le terrain et le fait de pouvoir lancer quelques milliers de tests unitaires sans mystère est inestimable et pose souvent des problèmes diagnostiqués en quelques minutes ... notamment des problèmes d'installation, flaky RAM problèmes spécifiques à une machine problèmes, problèmes de réseau floconneux, etc., etc. Je pense qu'il est extrêmement utile de mettre des tests sur le terrain. De plus, des problèmes aléatoires surgissent à des heures aléatoires et il est agréable d’avoir les tests unitaires assis en attente d’être exécutés à l’avance. L'espace disque est bon marché. Tout comme nous essayons de garder les données et les fonctions ensemble (conception OO), je pense qu'il y a quelque chose de précieux à garder le code et les tests ensemble (fonction + tests qui valident les fonctions).

Je souhaite placer mes tests dans le même projet en C #/.NET/Visual Studio 2008, mais je n’en ai pas encore fait la moindre analyse.

L'un des grands avantages de garder Foo.cs dans le même projet que FooTest.cs est que les développeurs sont constamment informés que lorsqu'une classe manque un test frère! Cela encourage de meilleures pratiques de codage basées sur des tests ... les trous sont plus apparents.

63
Dave

Placez les tests unitaires dans le même projet que le code pour obtenir une meilleure encapsulation.

Vous pouvez facilement tester les méthodes internes, ce qui signifie que vous ne rendrez pas publiques les méthodes qui auraient dû être internes.

En outre, il est vraiment agréable d’avoir les tests unitaires à proximité du code que vous écrivez. Lorsque vous écrivez une méthode, vous pouvez facilement trouver les tests unitaires correspondants car ils font partie du même projet. Lorsque vous construisez un assemblage comprenant des tests unitaires, toute erreur dans ce test vous générera une erreur de compilation. Vous devez donc maintenir votre version la plus récente à jour. Avoir unittest dans un projet séparé peut amener certains développeurs à oublier de construire le projet unittest et à rater les tests interrompus pendant un certain temps.

Et vous pouvez supprimer les tests unitaires du code de production en utilisant des balises de compilation (IF #Debug).

Les tests d'intégration automatiques (made i NUnit) doivent appartenir à un projet séparé, car ils n'appartiennent à aucun projet.

13
jenspo

Mes tests unitaires vont toujours dans un projet séparé. En fait, pour chaque projet de ma solution, il existe un projet de test séparé. Le code de test n'est pas un code d'application et ne doit pas être mêlé à celui-ci. L'un des avantages de les conserver dans des projets distincts - du moins à l'aide de TestDriven.Net - est que je peux cliquer avec le bouton droit sur un projet de test et exécuter tous les tests de ce projet, en testant une bibliothèque complète de code d'application en un seul clic.

10
tvanfosson

Si NUnit framework est utilisé, il existe une autre raison de placer les tests dans le même projet . Considérons l'exemple suivant de code de production mélangé avec des tests unitaires:

public static class Ext
{
     [TestCase(1.1, Result = 1)]
     [TestCase(0.9, Result = 1)]
     public static int ToRoundedInt(this double d)
     {
         return (int) Math.Round(d);
     }
}

Les tests unitaires servent ici de documentation et de spécification pour le code testé. Je ne sais pas comment obtenir cet effet d'auto-documentation, avec les tests situés dans un projet séparé. L'utilisateur de la fonction devrait rechercher les tests pour voir ces cas de test, ce qui est peu probable.

Update: Je sais qu'une telle utilisation de l'attribut TestCase n'était pas l'intention des développeurs de NUnit, mais pourquoi pas? 

7
Andrej Adamenko

Je fluctue entre un même projet et des projets différents.

Si vous publiez une bibliothèque, le code de test contenant le code de production pose un problème, sinon je constate que ce n'est généralement pas le cas (bien qu'il existe une barrière psychologique forte avant d'essayer).

Lorsque je place des tests dans le même projet, je trouve plus facile de basculer entre les tests et le code qu’ils testent, et plus facilement de les refactoriser/déplacer.

3
orip

Je les mets dans des projets séparés. Le nom de l'Assemblée reflète celui des espaces de noms, en règle générale pour nous. Par conséquent, s'il existe un projet appelé Company.Product.Feature.sln, il génère une sortie (nom d'assembly) de Company.Product.Feature.dll. Le projet de test est Company.Product.Feature.Tests.sln, générant Company.Product.Feature.Tests.dll.

Il est préférable de les conserver dans une solution unique et de contrôler la sortie via Configuration Manager. Nous avons une configuration nommée pour chacune des branches principales (Développement, Intégration, Production) au lieu d'utiliser les méthodes Debug et Release par défaut. Une fois que vous avez configuré vos configurations, vous pouvez les inclure ou les exclure en cochant la case "Construire" dans le Gestionnaire de configuration. (Pour obtenir le Gestionnaire de configuration, cliquez avec le bouton droit de la souris sur la solution et accédez à Gestionnaire de configuration.) Notez que le CM de Visual Studio présente parfois des erreurs. À plusieurs reprises, j'ai dû entrer dans les fichiers de projet et/ou de solution pour nettoyer les cibles créées.

De plus, si vous utilisez Team Build (et je suis sûr que les autres outils de génération .NET sont identiques), vous pouvez ensuite associer la construction à une configuration nommée. Cela signifie que si vous ne construisez pas vos tests unitaires pour votre construction "Production", par exemple, le projet de construction peut également connaître ce paramètre et ne pas le construire car ils ont été marqués comme tels.

De plus, nous avions l'habitude de faire des copies XCopy de la machine de construction. Le script omettrait simplement de copier tout élément nommé * .Tests.Dll en cours de déploiement. C'était simple, mais a fonctionné.

3
Joseph Ferris

Après avoir passé du temps dans des projets TypeScript, où les tests sont souvent placés dans un fichier à côté du code qu'ils testent, je suis parvenu à préférer cette approche à leur séparation:

  • Il est plus rapide de naviguer dans le fichier de test.
  • Il est plus facile de se rappeler de renommer les tests lorsque vous renommez la classe en cours de test.
  • Il est plus facile de se souvenir de déplacer les tests lorsque vous déplacez la classe en cours de test.
  • Il est immédiatement évident qu’il manque des tests à une classe.
  • Vous n'avez pas besoin de gérer deux structures de fichiers en double, une pour les tests et une pour le code.

C'est pourquoi, récemment, lorsque j'ai démarré un nouveau projet .NET Core, je voulais voir s'il était possible de reproduire cette structure dans un projet C # sans envoyer les tests ou les assemblys de test avec la version finale.

Mettre les lignes suivantes dans le fichier de projet semble bien fonctionner jusqu'à présent:

  <ItemGroup Condition="'$(Configuration)' == 'Release'">
    <Compile Remove="**\*.Tests.cs" />
  </ItemGroup>
  <ItemGroup Condition="'$(Configuration)' != 'Release'">
    <PackageReference Include="nunit" Version="3.11.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
  </ItemGroup>

Ceci garantit que, dans la configuration Release, tous les fichiers nommés *.Tests.cs sont exclus de la compilation et que les références de package de test unitaire requises sont également supprimées.

Si vous voulez toujours pouvoir tester les classes dans leur configuration de version, vous pouvez simplement créer une nouvelle configuration dérivée de Release appelée quelque chose comme ReleaseContainingTests.


Mise à jour: Après avoir utilisé cette technique pendant un moment, j'ai également trouvé utile de personnaliser vos icônes dans VS Code pour effectuer les tests (et autres). se démarquer un peu plus dans le volet Explorateur:

VS Code Screenshot

Pour ce faire, utilisez l’extension Material Icon Theme et ajoutez le code suivant à votre code JSON des préférences du code VS:

"material-icon-theme.files.associations": {
  "*.Tests.cs": "test-jsx",
  "*.Mocks.cs": "merlin",
  "*.Interface.cs": "yaml",
}
2
James Thurley

Je dirais de les garder séparés.

En plus des autres raisons mentionnées, le code et les tests combinés biaisent les numéros de couverture des tests. Lorsque vous indiquez la couverture des tests unitaires, la couverture déclarée est plus élevée car les tests sont couverts lorsque vous exécutez des tests unitaires. Lorsque vous indiquez la couverture des tests d'intégration, celle-ci est inférieure car les tests d'intégration ne permettent pas d'exécuter des tests unitaires.

1
Sebastian K

Je sais que la question est très ancienne, mais je voudrais ajouter mon expérience dans ce domaine. J’ai récemment changé l’habitude des tests unitaires de projets distincts en un seul. 

Pourquoi?

En premier lieu, je suis très enclin à garder la structure de dossiers principale du projet identique à celle du projet test. Donc, si j'ai un fichier sous Providers > DataProvider > SqlDataProvider.cs, je crée la même structure dans mes projets de test unitaire comme Providers > DataProvider > SqlDataProvider.Tests.cs

Mais lorsque le projet devient de plus en plus grand, lorsque vous déplacez des fichiers d’un dossier à un autre ou d’un projet à un autre, il devient un travail fastidieux de synchroniser ceux-ci avec des projets de test unitaire. 

Deuxièmement, il n’est pas toujours très facile de naviguer d’une classe à l’autre d’une classe à l’autre. Ceci est encore plus difficile pour JavaScript et Python.

Récemment, j'ai commencé à pratiquer cela, chaque fichier que j'ai créé (par exemple SqlDataProvider.cs), je crée un autre fichier avec le suffixe de test, comme SqlDataProvider.Tests.cs

Au début, il semble que cela va alourdir les fichiers et les références de bibliothèque, mais à long terme, vous éliminerez le syndrome de fichier en mouvement à première vue, et vous vous assurerez également que chaque fichier candidat à l’essai aura un fichier paire. avec le suffixe .Tests. Cela vous permet de sauter facilement dans le fichier de test (car il est côte à côte) au lieu de chercher dans un projet séparé.

Vous pouvez même écrire des règles commerciales pour parcourir le projet et identifier la classe qui ne possède pas de fichier .Tests, puis les signaler au propriétaire. Vous pouvez également indiquer facilement à votre programme d’essai de cibler les classes .Tests.

Surtout pour Js et Python, vous n’avez pas besoin d’importer vos références à partir d’un chemin différent, vous pouvez simplement utiliser le même chemin du fichier cible en cours de test.

J'utilise cette pratique depuis un moment et je pense que c'est un compromis très raisonnable entre la taille du projet vs la maintenabilité et la courbe d'apprentissage pour les nouveaux venus dans le projet.

0
Teoman shipahi

Je suis vraiment inspiré par le framework de tests unitaires de Flood NN Library de Robert Lopez. Il utilise un projet différent pour chaque classe d'unité testée et dispose d'une solution contenant tous ces projets, ainsi que d'un projet principal qui compile et exécute tous les tests.

La chose intéressante est aussi la mise en page du projet. Les fichiers source sont dans un dossier, mais le dossier pour le projet VS est ci-dessous. Cela vous permet de créer différents sous-dossiers pour différents compilateurs. Tous les projets VS sont livrés avec le code, il est donc très facile pour quiconque d’exécuter tout ou partie des tests unitaires.

0
user1115652