Je fais mes premiers pas avec des tests unitaires et je ne suis pas sûr de deux paradigmes qui semblent se contredire sur des tests unitaires, ce qui est:
être plus concret, j'ai un importateur que je veux tester. L'importateur a une fonction "importation", en prenant des données brutes (par exemple d'un CSV) et de renvoyer un objet d'un type certain qui sera également stocké dans une base de données via ORM (LINQTOSQL dans ce cas). P>
Maintenant, je veux tester plusieurs choses, par exemple que l'objet retourné renvoyé n'est pas null, que ce sont des champs obligatoires ne sont pas nuls ni vides et que les attributs sont corrects. J'ai écrit 3 tests unitaires pour cela. Devrait chaque test d'importer et d'obtenir le travail ou cela appartient-il à une logique générale? D'autre part, Croyant ce blog Publier , Ce dernier serait une mauvaise idée aussi loin que mon compréhension va. Aussi, cela ne violerait-il pas le confinement de soi? P>
Ma classe ressemble à ceci: P>
[TestFixture]
public class ImportJob
{
private TransactionScope scope;
private CsvImporter csvImporter;
private readonly string[] row = { "" };
public ImportJob()
{
CsvReader reader = new CsvReader(new StreamReader(
@"C:\SomePath\unit_test.csv", Encoding.Default),
false, ';');
reader.MissingFieldAction = MissingFieldAction.ReplaceByEmpty;
int fieldCount = reader.FieldCount;
row = new string[fieldCount];
reader.ReadNextRecord();
reader.CopyCurrentRecordTo(row);
}
[SetUp]
public void SetUp()
{
scope = new TransactionScope();
csvImporter = new CsvImporter();
}
[TearDown]
public void TearDown()
{
scope.Dispose();
}
[Test]
public void ImportJob_IsNotNull()
{
Job j = csvImporter.ImportJob(row);
Assert.IsNotNull(j);
}
[Test]
public void ImportJob_MandatoryFields_AreNotNull()
{
Job j = csvImporter.ImportJob(row);
Assert.IsNotNull(j.Customer);
Assert.IsNotNull(j.DateCreated);
Assert.IsNotNull(j.OrderNo);
}
[Test]
public void ImportJob_MandatoryFields_AreValid()
{
Job j = csvImporter.ImportJob(row);
Customer c = csvImporter.GetCustomer("01-01234567");
Assert.AreEqual(j.Customer, c);
Assert.That(j.DateCreated.Date == DateTime.Now.Date);
Assert.That(j.OrderNo == row[(int)Csv.RechNmrPruef]);
}
// etc. ...
}
5 Réponses :
Vos classes de test ne sont pas différentes des classes habituelles et doivent être traitées comme telles: toutes les bonnes pratiques (sèche, la réutilisation du code, etc.) doivent également l'appliquer également. P>
Si vous déplacez dans la fonction de configuration ou non, il sera toujours exécuté avant que chaque test soit exécuté. Si vous avez la même ligne exacte em> la même ligne en haut de chaque test, il est simplement logique que vous déplacez cette ligne dans la partie de configuration. P> La saisie du blog que vous avez posté se sont plaints de la configuration des valeurs de test effectuées dans une fonction déconnectée (éventuellement pas sur le même écran que) du test lui-même - mais votre cas est différent, en ce que les données de test sont entraînées par un fichier texte externe, de sorte que Cette plainte ne correspond pas non plus à votre cas d'utilisation spécifique. p> p>
vous pourriez mettre le Job j = csvimporter.importjob (rangée); dans votre configuration. De cette façon, vous ne répétez pas le code. P>
Vous devez réellement exécuter cette ligne de code pour chaque test. Sinon, les tests commenceront à échouer à cause de choses qui se sont produites dans d'autres tests. Cela deviendra difficile à maintenir. P>
Le problème de performance n'est pas causé par des violations sèches. Vous devriez réellement tout configurer pour chaque test. Ce ne sont pas des tests unitaires, ils sont des tests d'intégration, vous comptez sur des fichiers externes pour exécuter le test. Vous pouvez rendre ImportJob lu à partir d'un flux au lieu d'ouvrir directement un fichier. Ensuite, vous pouvez tester avec une mémoire Mémoire. P>
Cela dépend de la quantité de votre scénario commun à votre test. Dans la publication du blog, vous avez référence à la plainte principale, c'est que la méthode de configuration a fait une configuration différente pour les trois tests et qui ne peut pas être considérée comme une meilleure pratique. Dans votre cas, vous avez la même configuration pour chaque test / scénario, puis vous devez utiliser une configuration partagée au lieu de dupliquer le code dans chaque test. Si vous trouvez plus tard qu'il y a plus de tests qui ne partagent pas cette configuration ou nécessite une configuration différente partagée entre un ensemble de tests, le refacteur de la nouvelle classe de cas de test. Vous pouvez également avoir des méthodes de configuration partagées non marquées avec [Configuration] mais sont appelées au début de chaque test qui en a besoin: un moyen de trouver le bon mélange pourrait être de commencer Sans méthode de configuration et lorsque vous constatez que vous êtes duplication Code pour la configuration du test, refacteur à une méthode partagée. P> P>
Dans l'un de mes projets, nous avons convenu avec l'équipe que Nous ne mettrons aucune logique d'initialisation dans les constructeurs de tests d'unité forts>. Nous avons la configuration, TestFixTureSetup, Setupfixture (depuis la version 2.4 des Nunit). Ils suffisent pour presque tous les cas lorsque nous avons besoin d'initialisation. Nous forcions les développeurs à utiliser l'un de ces attributs et de définir explicitement si nous exécuterons ce code d'initialisation avant chaque test, avant tous les tests dans un appareil ou avant tous les tests d'un espace de noms. p>
Cependant, je suis en désaccord pour que les tests unitaires devraient toujours confirmer toutes les bonnes pratiques supposées pour un développement habituel. C'est souhaitable, mais ce n'est pas une règle. Mon point est que le client de la vie réelle Je suppose que cela dépend du projet, et le PM ou le responsable de l'équipe doit planifier et estimer la qualité des tests de l'unité, leur complétude et leur couverture de code, comme si elles estiment toutes les autres fonctionnalités commerciales de votre produit. Mon opinion, qu'il est préférable d'avoir des tests d'unités de copier-coller qui couvrent 80% du code de production puis de disposer de tests unitaires très bien conçus et séparés qui ne couvrent que 20%. P>
J'ai déclaré que le champ «rangée» est libéonnement, ce qui signifie que je avoir b> pour l'initialiser dans le constructeur. Je préférerais que cela reste réadon, car cela enverra le danger de tout test de modification de «rangée». Comment résoudriez-vous cela?
Vous pouvez l'initialiser lors de la déclaration de la chaîne de chaîne libératrice privée variable [] ligne = {vos valeurs ici}; De plus, vous n'avez peut-être pas du tout de champ privé. Pourquoi ne pas avoir une ligne de variables locales différentes pour différentes méthodes de test avec différentes valeurs (cela pourrait augmenter une couverture de code)?
Et les pls ne prennent pas comme une règle commune ce que j'ai dit à propos de n'utilisant pas de constructeurs. C'était la décision de notre équipe de développement pour un projet particulier. Nous l'avons trouvé pratique. Quant à votre propre test de l'unité, je dirais que le test est déjà assez bon et que vous essayez maintenant de le faire "parfait". Malheureusement, dans la plupart des projets, nous n'avons pas le temps d'écrire un test d'unité parfait, nous ne les écrivons que jusqu'à ce qu'ils soient «assez bons». Par exemple, je ne me soucierais pas de faire des champs réadis. Si j'écris occasionnellement un code qui modifiera une ligne et que mon test échouera, je l'exécuterai et trouvera une erreur et une solution.