c# asp.net - Comment faire des tests d'intégration dans.NET avec de vrais fichiers?




core integration (5)

J'ai quelques classes qui implémente une certaine logique liée au système de fichiers et aux fichiers. Par exemple, j'effectue les tâches suivantes dans le cadre de cette logique:

  • vérifier si un certain dossier a une certaine structure (par exemple, il contient des sous-dossiers avec des noms spécifiques etc ...)
  • chargement de certains fichiers à partir de ces dossiers et vérification de leur structure (par exemple, ce sont des fichiers de configuration, situés à certains endroits dans certains dossiers)
  • charger des fichiers supplémentaires pour le test / la validation à partir du fichier de configuration (par exemple, ce fichier de configuration contient des informations sur d'autres fichiers dans le même dossier, qui devraient avoir d'autres structures internes etc ...)

Maintenant, toute cette logique a un flux de travail et des exceptions sont levées, si quelque chose ne va pas (par exemple, le fichier de configuration n'est pas trouvé à l'emplacement du dossier spécifique). De plus, il y a le MEF (Managed Extensibility Framework) impliqué dans cette logique, car certains de ces fichiers que je vérifie sont des DLL managées que je charge manuellement sur des agrégats MEF etc ...

Maintenant, j'aimerais tester tout ça d'une manière ou d'une autre. Je pensais créer plusieurs dossiers de test physiques sur le disque dur, qui couvrent différents cas de test et ensuite exécuter mon code contre eux. Je pourrais créer par exemple:

  • dossier avec la structure correcte et tous les fichiers étant valides
  • dossier avec la structure correcte, mais avec un fichier de configuration invalide
  • dossier avec structure correcte mais fichier de configuration manquant etc ...

Serait-ce la bonne approche? Je ne suis pas sûr cependant comment exécuter exactement mon code dans ce scénario ... Je ne veux certainement pas courir l'application entière et le pointer pour vérifier ces dossiers moqués. Dois-je utiliser un cadre de test unitaire pour écrire des "tests unitaires", qui exécute mon code par rapport à ces objets du système de fichiers?

En général, est-ce une approche correcte pour ce genre de scénarios de test? Y a-t-il d'autres meilleures approches?


Answers

Je ne connais pas l'architecture de votre programme pour donner un bon conseil, mais je vais essayer

  1. Je crois que vous n'avez pas besoin de tester la structure réelle du fichier . Les services d'accès aux fichiers sont définis par le système / l'infrastructure, et ils n'ont pas besoin d'être testés. Vous devez vous moquer de ces services dans des tests connexes.
  2. Aussi, vous n'avez pas besoin de tester MEF. Il est déjà testé.
  3. Utilisez les principes SOLID pour effectuer des tests unitaires. Surtout regarder le principe de responsabilité unique cela vous permettra de créer des tests unitaires, qui ne seront pas liés les uns aux autres. N'oubliez pas de vous moquer pour éviter les dépendances.
  4. Pour effectuer des tests d'intégration, vous pouvez créer un ensemble de classes auxiliaires, qui émuleront des scénarios de structures de fichiers que vous souhaitez tester. Cela vous permettra de ne pas rester attaché à la machine sur laquelle vous exécuterez ces tests. Une telle approche peut-être plus compliquée que de créer une structure de fichiers réelle, mais j'aime ça.

J'irais avec un dossier de test unique. Pour différents cas de test, vous pouvez placer différents fichiers valides / invalides dans ce dossier dans le cadre de la configuration du contexte. Dans le test de démontage, supprimez simplement ces fichiers du dossier.

Par exemple avec Specflow :

Given configuration file not exist
When something
Then foo

Given configuration file exists
And some dll not exists
When something
Then bar

Définissez chaque étape de configuration du contexte en copiant / ne copiant pas le fichier approprié dans votre dossier. Vous pouvez également utiliser la table pour définir quel fichier doit être copié dans le dossier:

Given some scenario
| FileName         |
| a.config         |
| b.invalid.config |
When something
Then foobar

Je construirais une logique de framework et testerais les problèmes de concurrence et les exceptions de système de fichiers pour assurer un environnement de test bien défini.

Essayez d'énumérer toutes les limites du domaine du problème. S'il y en a trop, réfléchissez à la possibilité que votre problème soit défini de manière trop large et qu'il doive être décomposé. Quel est l'ensemble des conditions nécessaires et suffisantes pour que votre système réussisse tous les tests? Puis examinez chaque condition et traitez-la comme un point d'attaque individuel. Et dressez la liste de toutes les façons dont vous pouvez penser, d'enfreindre cela. Essayez de vous prouver que vous les avez tous trouvés. Ensuite, écrivez un test pour chacun.

Je voudrais d'abord passer par le processus ci-dessus pour l'environnement, construire et tester d'abord à un niveau satisfaisant, puis pour la logique plus détaillée dans le flux de travail. Certaines itérations peuvent être nécessaires si des dépendances entre l'environnement et la logique détaillée vous apparaissent lors des tests.


Vous devriez tester autant de logique que possible avec des tests unitaires, en faisant abstraction des appels au système de fichiers derrière les interfaces. L'utilisation de l'injection de dépendances et d'un framework de test tel que FakeItEasy vous permettra de tester que vos interfaces sont réellement utilisées / appelées pour fonctionner sur les fichiers et les dossiers.

À un certain moment cependant, vous devrez tester les implémentations qui fonctionnent sur le système de fichiers, et c'est là que vous aurez besoin de tests d'intégration.

Les choses que vous devez tester semblent être relativement isolées puisque tout ce que vous voulez tester est vos propres fichiers et répertoires, sur votre propre système de fichiers. Si vous vouliez tester une base de données, ou un autre système externe avec plusieurs utilisateurs, etc., les choses pourraient être plus compliquées.

Je ne pense pas que vous trouverez des «règles officielles» pour la meilleure façon de faire des tests d'intégration de ce type, mais je crois que vous êtes sur la bonne voie. Quelques idées que vous devriez vous efforcer de:

  • Normes claires: Définissez clairement les règles et le but de chaque test.
  • Automatisation: Possibilité de réexécuter les tests rapidement et sans trop de modifications manuelles.
  • Répétabilité: Une situation de test que vous pouvez "réinitialiser", de sorte que vous pouvez réexécuter les tests rapidement, avec seulement de légères variations.

Créer un scénario de test reproductible

Dans votre situation, je voudrais mettre en place deux dossiers principaux: Un dans lequel tout est comme il est censé être (c'est-à-dire fonctionne correctement), et celui dans lequel toutes les règles sont brisées.

Je voudrais créer ces dossiers et tous les fichiers en eux, puis zip chacun des dossiers, et écrire la logique dans une classe de test pour décompresser chacun d'eux.

Ce ne sont pas vraiment des tests; Considérez-les plutôt comme des «scripts» pour configurer votre scénario de test, vous permettant de supprimer et de recréer facilement et rapidement vos dossiers et fichiers, même si vos principaux tests d'intégration doivent être modifiés ou gâchés lors des tests. La raison de les mettre dans une classe de test est simplement de rendre alors facile à exécuter à partir de la même interface que vous travaillerez pendant les tests.

Essai

Créez deux ensembles de classes de test, un ensemble pour chaque situation (dossier correctement configuré par rapport au dossier avec des règles cassées). Placez ces tests dans une hiérarchie de dossiers qui vous semble significatif (en fonction de la complexité de votre situation).

On ne sait pas à quel point vous êtes familiarisé avec les tests d'unité / d'intégration. En tout cas, je recommanderais NUnit . J'aime utiliser les extensions de Should aussi. Vous pouvez obtenir les deux de Nuget:

install-package Nunit
install-package Should

Le paquet devrait vous permettre d'écrire le code de test de la manière suivante:

someCalculatedIntValue.ShouldEqual(3); 
someFoundBoolValue.ShouldBeTrue();

Notez qu'il existe plusieurs testeurs disponibles pour exécuter vos tests. Personnellement, j'ai seulement eu une expérience réelle avec le coureur intégré dans Resharper, mais j'en suis assez satisfait et je n'ai aucun problème à le recommander.

Voici un exemple de classe de test simple avec deux tests. Notez que dans le premier, nous vérifions une valeur attendue en utilisant une méthode d'extension de Should, alors que nous ne testons explicitement rien dans la seconde. En effet, il est marqué avec [ExpectedException], ce qui signifie qu'il échouera si une exception du type spécifié n'est pas lancée lors de l'exécution du test. Vous pouvez l'utiliser pour vérifier qu'une exception appropriée est levée lorsque l'une de vos règles est rompue.

[TestFixture] 
public class When_calculating_sums
{                    
    private MyCalculator _calc;
    private int _result;

    [SetUp] // Runs before each test
    public void SetUp() 
    {
        // Create an instance of the class to test:
        _calc = new MyCalculator();

        // Logic to test the result of:
        _result = _calc.Add(1, 1);
    }

    [Test] // First test
    public void Should_return_correct_sum() 
    {
        _result.ShouldEqual(2);
    }

    [Test] // Second test
    [ExpectedException(typeof (DivideByZeroException))]
    public void Should_throw_exception_for_invalid_values() 
    {
        // Divide by 0 should throw a DivideByZeroException:
        var otherResult = _calc.Divide(5, 0);
    }       

    [TearDown] // Runs after each test (seldom needed in practice)
    public void TearDown() 
    {
        _calc.Dispose(); 
    }
}

Une fois tout cela en place, vous devriez être capable de créer et de recréer des scénarios de test, et d'exécuter des tests de manière simple et répétable.

Edit: Comme indiqué dans un commentaire, Assert.Throws () est une autre option pour s'assurer que les exceptions sont levées comme requis. Personnellement, j'aime la variante de tag, et avec les paramètres , vous pouvez aussi vérifier des choses comme le message d'erreur. Un autre exemple (en supposant qu'un message d'erreur personnalisé est lancé à partir de votre calculatrice):

[ExpectedException(typeof(DivideByZeroException), ExpectedMessage="Attempted to divide by zero" )]
public void When_attempting_something_silly(){  
    ...
}

Les types de variable Decimal, Double et Float sont différents dans la façon dont ils stockent les valeurs. La précision est la principale différence lorsque float est un type de données à virgule flottante simple précision (32 bits), double est un type de données à virgule flottante double précision (64 bits) et décimal est un type de données à virgule flottante de 128 bits.

Flotteur - 32 bits (7 chiffres)

Double - 64 bits (15-16 chiffres)

Décimal - 128 bits (28 à 29 chiffres significatifs)

La principale différence est que Floats et Doubles sont des types à virgule flottante binaire et un Decimal stocke la valeur sous forme de virgule décimale flottante. Les décimales ont donc une précision beaucoup plus grande et sont généralement utilisées dans des applications de calcul monétaires (financières) ou scientifiques nécessitant un haut degré de précision. Mais en termes de performances, les nombres décimaux sont plus lents que les types double et flottant.

Décimal peut 100% représenter avec précision n'importe quel nombre dans la précision du format décimal, tandis que Float et Double, ne peut pas représenter avec précision tous les nombres, même les chiffres qui sont dans leurs formats respectifs de précision.

Décimal

Dans le cas d'applications financières, ou de calculs scientifiques, il est préférable d'utiliser les types décimaux car cela vous donne un haut niveau de précision et évite facilement les erreurs d'arrondi

Double

Les types doubles sont probablement le type de données le plus couramment utilisé pour les valeurs réelles, à l'exception du traitement de l'argent.

Flotte

Il est principalement utilisé dans les bibliothèques graphiques car les demandes de puissances de traitement sont très élevées et les situations d'utilisation peuvent supporter des erreurs d'arrondi.





c# .net file unit-testing integration-testing