Je teste une méthode qui consiste à générer une collection d'objets de données. Je veux vérifier que les propriétés des objets sont définies correctement. Certaines propriétés seront définies sur la même chose; d'autres seront définis sur une valeur qui dépend de leur position dans la collection. La façon naturelle de le faire semble être une boucle. Cependant, Roy Osherove déconseille fortement d'utiliser la logique dans les tests unitaires ( Art of Unit Testing , 178). Il dit:
Un test qui contient de la logique teste généralement plus d'une chose à la fois, ce qui n'est pas recommandé, car le test est moins lisible et plus fragile. Mais la logique de test ajoute également de la complexité qui peut contenir un bogue caché.
Les tests doivent, en règle générale, être une série d'appels de méthode sans flux de contrôle, pas même
try-catch
, et avec des appels d'assertion.
Cependant, je ne vois rien de mal dans ma conception (comment générer autrement une liste d'objets de données, dont certaines valeurs dépendent de leur emplacement dans la séquence? - je ne peux pas exactement les générer et les tester séparément). Y a-t-il quelque chose qui ne respecte pas les tests dans ma conception? Ou suis-je trop attaché à l'enseignement d'Osherove? Ou y a-t-il une magie secrète de test unitaire que je ne connais pas qui contourne ce problème? (J'écris en C # / VS2010 / NUnit, mais je recherche des réponses indépendantes du langage si possible.)
la source
in
) nécessaire, si le test est "Frob a été ajouté avec succès à une collection existante".toString()
la collection et de la comparer à ce qu'elle devrait être. Simple et fonctionne.Réponses:
TL; DR:
La première chose à tester est que le dogme est inutile. J'aime lire The Way of Testivus qui souligne certains problèmes avec le dogme d'une manière légère.
Si le test doit être écrit d'une manière ou d'une autre, écrivez-le de cette façon. Tenter de forcer le test dans une disposition de test idéalisée ou de ne pas l'avoir du tout n'est pas une bonne chose. Avoir un test aujourd'hui qui teste c'est mieux que d'avoir un test "parfait" un jour plus tard.
Je vais également souligner le bit sur le test laid:
Ceux-ci peuvent être considérés comme des truismes pour ceux qui suivent depuis longtemps ... et ils deviennent juste enracinés dans la manière de penser et d'écrire des tests. Pour les personnes qui ne l'ont pas été et tentent d'en arriver là, les rappels peuvent être utiles (je trouve même que les relire m'aide à éviter de m'enfermer dans un dogme).
Considérez cela lors de l'écriture d'un test moche, si le code peut être une indication que le code essaie de faire trop trop. Si le code que vous testez est trop complexe pour être correctement exercé en écrivant un test simple, vous pouvez envisager de diviser le code en parties plus petites qui peuvent être testées avec les tests les plus simples. Il ne faut pas écrire un test unitaire qui fait tout (ce n'est peut-être pas un test unitaire alors). Tout comme les «objets divins» sont mauvais, les «tests unitaires divins» sont également mauvais et devraient être des indications pour revenir en arrière et revoir le code.
Vous devriez pouvoir exercer tout le code avec une couverture raisonnable grâce à des tests aussi simples. Les tests qui effectuent davantage de tests de bout en bout qui traitent de questions plus importantes («J'ai cet objet, rassemblé en XML, envoyé au service Web, via les règles, annulé et non corrigé») est un excellent test - mais il ne l'est certainement pas. 't un test unitaire (et tombe dans le domaine des tests d'intégration - même s'il a des services moqués qu'il appelle et personnalisé dans les bases de données de mémoire pour faire le test). Il peut toujours utiliser le framework XUnit pour les tests, mais le framework de tests n'en fait pas un test unitaire.
la source
J'ajoute une nouvelle réponse parce que mon point de vue est différent de lorsque j'ai écrit la question et la réponse d'origine; il n'est pas logique de les mailler ensemble en un seul.
J'ai dit dans la question d'origine
C'est là que je me suis trompé. Après avoir fait de la programmation fonctionnelle au cours de la dernière année, je me rends compte maintenant que j'avais juste besoin d'une opération de collecte avec un accumulateur. Ensuite, je pouvais écrire ma fonction comme une fonction pure qui fonctionnait sur une chose et utiliser une fonction de bibliothèque standard pour l'appliquer à la collection.
Donc ma nouvelle réponse est: utilisez des techniques de programmation fonctionnelles et vous éviterez ce problème entièrement la plupart du temps. Vous pouvez écrire vos fonctions pour opérer sur des choses uniques et les appliquer uniquement à des collections de choses au dernier moment. Mais s'ils sont purs, vous pouvez les tester sans référence aux collections.
Pour une logique plus complexe, s'appuyer sur des tests basés sur les propriétés . Lorsqu'ils ont une logique, celle-ci doit être inférieure et inverse à la logique du code testé, et chaque test vérifie tellement plus qu'un test unitaire basé sur le cas que la petite quantité de logique en vaut la peine.
Surtout s'appuyer toujours sur vos types . Obtenez les types les plus forts que vous pouvez et utilisez-les à votre avantage. Cela réduira le nombre de tests que vous devez écrire en premier lieu.
la source
N'essayez pas de tester trop de choses à la fois. Chacune des propriétés de chaque objet de données dans la collection est trop pour un test. Au lieu de cela, je recommande:
Le faire de cette façon rend les tests suffisamment petits pour que laisser de côté les boucles ne semble pas douloureux. Exemple C # / unité, méthode donnée en cours de test
ICollection<Foo> generateFoos(uint numberOfFoos)
:Si vous êtes habitué au paradigme du «test unitaire plat» (pas de structures / logique imbriquées), ces tests semblent assez propres. Ainsi, la logique est évitée dans les tests en identifiant le problème d'origine comme essayant de tester trop de propriétés à la fois, plutôt que de manquer de boucles.
la source