Développement piloté par les tests: un bon moyen / accepté de tester les opérations du système de fichiers?

14

Je travaille actuellement sur un projet qui génère une table (entre autres) basée sur le contenu d'un système de fichiers, et à son tour fait des modifications de métadonnées sur les choses qu'il trouve. La question est: comment les tests doivent-ils être écrits autour de cela, ou mis en place? Existe-t-il un moyen simple de se moquer de cela? Ou dois-je configurer un "bac à sable"?

Kirbinator
la source

Réponses:

13

Comme vous le faites toujours dans TDD avec des ressources externes: vous créez une ou plusieurs interfaces pour vos opérations de système de fichiers et vous les "moquez". Vous voulez tester votre "générateur de table" et votre code de modification des métadonnées, pas les opérations du système de fichiers lui-même (très probablement, vous utilisez des implémentations de bibliothèque prêtes à l'emploi pour accéder au système de fichiers).

Doc Brown
la source
TDD ne recommande pas de se moquer de la mise en œuvre de l'unité testée. Voir (e, g) solnic.eu/2014/05/22/mocking-and-ruby.html
soru
1
@soru: Ce n'est pas ce que cette réponse recommande. Il recommande d'abord de créer des interfaces, puis de se moquer de l' interface . Vous testez donc la logique métier, mais pas l'interface du système de fichiers.
sleske
5
Mais il semble que la logique métier soit définie en termes de fichiers et de répertoires. Donc, le truc qui appelle l'API du système de fichiers est le truc qui doit être testé. Le test de toute logique métier non liée ne nécessite pas de simulation; il suffit de le tester.
soru
@soru à droite, donc vous créez une couche mince autour des fichiers et des dossiers avec une interface définie de telle sorte que toutes les opérations spécifiques au domaine soient du côté client, et le côté de l'implémentation est assez trivial pour être sûr qu'il fonctionne sans test unitaire (les tests d'intégration encore nécessaire). Semblable à l'idée d'un Humble Dialog en code ui, ou en utilisant un faux référentiel en code qui fonctionne sur des objets persistants.
Jules
2
Donc, effectivement, nous renonçons à tester la classe réelle qui interagit avec le système de fichiers, la base de données, etc. ... au lieu de cela, nous créons une implémentation différente avec la même interface qu'un mock / stub, mais la classe réelle que nous quittons sans aucun type de test unitaire, parce que nous pensons que nous ne pouvons pas le tester à l'unité et que nous devrions plutôt faire des tests d'intégration pour le tester. Est-ce correct?
Andrew Savinykh
11

Quel est le problème avec un système de fichiers "test"?

Créez une structure de dossiers / répertoires de modèle qui contient suffisamment de contenu pour tester vos opérations.

Lors de la configuration de votre test unitaire, copiez cette structure initiale (vous recommandons de ZIP le modèle et décompressez dans votre zone de test). Exécutez vos tests. Supprimez le tout pendant le démontage.

Le problème de la moquerie est que les systèmes de fichiers, les systèmes d'exploitation et les bases de données qui appartiennent à votre projet ne sont pas vraiment considérés comme des ressources externes et que la moquerie des appels système de bas niveau est à la fois longue et sujette aux erreurs.

James Anderson
la source
5
Le fait de se moquer des opérations du système de fichiers créera des tests d'exécution beaucoup (!) Plus rapides que l'utilisation d'un vrai système de fichiers, et si cela est plus "sujet aux erreurs" est discutable, je dirais que cela dépend de l'implémentation. Néanmoins, je pense que votre suggestion est bonne pour créer des tests d' intégration automatisés (ce que je ferais généralement en premier lorsque je ne fais pas TDD). Mais l'OP a spécifiquement demandé TDD, et les tests unitaires TDD doivent être rapides.
Doc Brown
1
Je pense que les systèmes de fichiers, s'ils sont moqués, devraient idéalement avoir une API entière écrite et maintenue par un groupe, car vous réinventez la roue, si vous faites quelque chose d'important avec le système de fichiers.
Frank Hileman
2
@Doc Brown - Je suppose en quelque sorte qu'il veut faire des opérations de type dir, delete et renommer qui ont toutes des cas de bord qui seraient difficiles à moquer. De plus, sur le matériel moderne, décompresser quelques petits fichiers dans un répertoire est un peu plus lent que de charger une classe Java - c'est tout IO après tout.
James Anderson
Plus je pense au système de fichiers de test, plus je l'aime.
Frank Hileman
3

C'est le genre de chose dont vous avez absolument besoin pour tester l'intégration, car les systèmes de fichiers du monde réel ont toutes sortes de comportements étranges (comme la façon dont Windows ne permet pas de supprimer un fichier si un processus, y compris le suppresseur, l'ouvre).

L'approche TDD consiste donc à écrire d'abord le test d'intégration (TDD, à proprement parler, n'a pas de concepts distincts de «test unitaire» et de «test d'intégration»; ce ne sont que des tests). Très probablement, ce sera suffisant; alors travail fait, arrêtez, rentrez chez vous .

Sinon, il y aura une certaine complexité interne qui n'est pas facile à tester correctement en organisant les fichiers. Dans ce cas, vous supprimez simplement cette complexité, la mettez dans une classe et écrivez des tests unitaires pour cette classe . Très probablement, vous constaterez que cette classe commune est également utilisable dans la base de données, le fichier xml, etc.

En aucun cas, vous ne prendrez le noyau fondamental du code que vous écrivez et ne vous en moquerez pour écrire des tests qui réussiront, que l'unité testée soit mauvaise ou non.

Soru
la source
Cette réponse a vraiment mis en perspective pour moi - 'unit test' and 'integration test'; they are just tests.je pense que ce sera la meilleure solution pour mon cas - j'ai vraiment besoin de tester les bibliothèques de système de fichiers que j'utilise pour les cas marginaux, et comment l'application devrait répondre à ceux. Si je passe à une bibliothèque de système de fichiers différente, je ne veux pas avoir à réécrire un tas de mocks / code de test pour travailler avec la nouvelle bibliothèque, mais avoir une structure de dossier de test et des tests d'intégration rendrait cela beaucoup plus simple.
tehDorf
2

Je comprends votre question comme "un bon moyen / accepté de tester une classe qui dépend des opérations du système de fichiers". Je ne suppose pas que vous souhaitez tester le système de fichiers de votre système d'exploitation.

Afin de maintenir l'effort pour «s'interfacer avec vos opérations sur le système de fichiers et les« simuler »comme le suggère la réponse de @Doc Brown, il est préférable d'utiliser des flux binaires java ou un lecteur de texte (ou un équivalent en c # ou le langage de programmation que vous utilisez) au lieu d'utiliser des fichiers avec des noms de fichiers directement dans votre classe développée par tdd.

Exemple:

En utilisant java, j'ai implémenté une classe CsvReader

public class CsvReader {
    private Reader reader;

    public CsvReader(Reader reader) {
        this.reader = reader;
    }
}

Pour tester, j'ai utilisé des données en mémoire comme celle-ci

String contentOfCsv = "TestColumn1;TestColumn2\n"+
    "value1;value2\n";

CsvReader sut = new CsvReader(java.io.StringReader(contentOfCsv));

ou intégrer des données de test dans les ressources

CsvReader sut = new CsvReader(getClass().getResourceAsStream("/data.csv"));

En production j'utilise le système de fichiers

CsvReader sut = new CsvReader(new BufferedReader( new FileReader( "/import/Prices.csv" ) ));

De cette façon, mon CsvReader ne dépend pas du système de fichiers mais d'un abstraction "Reader" où il y a une implémentation pour le système de fichiers.

k3b
la source
2
Le seul problème ici est que l'OP ne parlait pas d'opérations de fichiers, mais d' opérations de système de fichiers et d'opérations de métadonnées - je suppose qu'il voulait dire quelque chose comme répertorier tous les fichiers dans un répertoire, mettre à jour certaines informations EXIF ​​dans tous les fichiers image, etc.
Doc Brown
C'est correct.
Kirbinator
1
Vous pouvez créer IDirectoryLister qui a une méthode String [] List (répertoire String); alors FakeDirectoryLister peut implémenter cette méthode en renvoyant simplement une nouvelle chaîne [] {".", "..", "foo.bat", "bar.exe"};
Anders Lindén
0

Créez un wrapper pour les opérations du système de fichiers. Dans les tests, passez une maquette qui implémente la même interface que le wrapper. En production, passez dans l'emballage.

jhewlett
la source