Stratégies pour les tests unitaires et le développement piloté par les tests

16

Je suis un ardent défenseur du développement piloté par les tests en informatique scientifique. Son utilité dans la pratique est tout simplement stupéfiante et atténue vraiment les problèmes classiques que les développeurs de code connaissent. Cependant, il y a des difficultés inhérentes à tester des codes scientifiques qui ne sont pas rencontrés dans la programmation générale, donc les textes TDD ne sont pas terriblement utiles comme tutoriels. Par exemple:

  • En général, vous ne connaissez pas a priori une réponse exacte à un problème complexe donné, alors comment pouvez-vous écrire un test?

  • Le degré de parallélisme change; J'ai récemment rencontré un bogue où l'utilisation de tâches MPI en tant que multiple de 3 échouait, mais un multiple de 2 fonctionnait. De plus, les frameworks de test communs ne semblent pas très compatibles avec MPI en raison de la nature même de MPI - vous devez réexécuter un binaire de test pour modifier le nombre de tâches.

  • Les codes scientifiques comportent souvent de nombreuses pièces étroitement couplées, interdépendantes et interchangeables. Nous avons tous vu le code hérité, et nous savons combien il est tentant de renoncer à une bonne conception et d'utiliser des variables globales.

  • Souvent, une méthode numérique peut être une "expérience", ou le codeur ne comprend pas complètement comment il fonctionne et essaie de le comprendre, il est donc impossible d'anticiper les résultats.

Quelques exemples de tests que j'écris pour du code scientifique:

  • Pour les intégrateurs de temps, utilisez un ODE simple avec une solution exacte et testez que votre intégrateur le résout avec une précision donnée, et l'ordre de précision est correct en testant avec différentes tailles d'étape.

  • Tests de stabilité zéro: vérifier qu'une méthode avec 0 conditions limites / initiales reste à 0.

  • Tests d'interpolation: étant donné une fonction linéaire, assurez-vous qu'une interpolation est correcte.

  • Validation héritée: isolez un morceau de code dans une application héritée connue pour être correcte et extrayez des valeurs discrètes à utiliser pour les tests.

Il arrive souvent que je n'arrive pas à comprendre comment tester correctement un morceau de code donné, à part les essais et erreurs manuels. Pouvez-vous fournir des exemples de tests que vous écrivez pour le code numérique et / ou des stratégies générales pour tester des logiciels scientifiques?

Aurelius
la source
Pourriez-vous, s'il vous plaît, clarifier ce que vous entendez par tests d'interpolation?
Dmitry Kabanov

Réponses:

8

Méthode des solutions fabriquées .

Vérifier par des études de raffinement que la méthode atteint l'ordre théorique de précision.

Conservation de la réponse. Reproduction des solutions au niveau du bit et de la norme.

Bill Barth
la source
Je voulais mentionner MMS dans le message d'origine; c'est bon pour la vérification du code, mais du point de vue des tests unitaires, cela ne vaut rien. Si ces tests échouent, il ne fournit aucune indication quant à où et pourquoi.
Aurelius
3
@Aurelius: Mais c'est une excellente stratégie pour un développement piloté par les tests! Pour les codes PDE / ODE / Algèbre linéaire, vous devez écrire très petit tests MMS pouvant s'exécuter en moins d'une seconde. Lorsque vous apportez une modification, vous les exécutez. S'ils se cassent, vous avez fait quelque chose de mal! Vous seriez surpris de voir combien2×2×2-le problème d'élément peut vous le dire (ou autre).
Bill Barth
Une grande partie de la littérature que j'ai vue sur le MMS est essentiellement des solutions globales, par exemple pour les problèmes de CFD, une solution fabriquée pourrait être une analyse de profil aérodynamique. Lorsque ce test échoue, au mieux vous avez réduit le coupable à 5000 lignes de code, donc cela ne vaut pas la peine pour TDD - vous n'avez aucune idée de l'endroit où l'échec réel se produit. Je suis d'accord qu'un problème 2x2x2 est extrêmement précieux, et je les utilise beaucoup personnellement. Mais il est assez courant que je rencontre des problèmes qui n'apparaissent qu'avec des systèmes plus grands; J'ai trouvé récemment un bogue du compilateur ifort qui ne se manifestait que par de gros problèmes.
Aurelius
@Aurelius: Aucun argument ici. Vous devriez avoir une gamme de tests et les exécuter tous fréquemment.
Bill Barth
@Aurelius À la valeur nominale, le MMS n'est pas un test unitaire, mais un test fonctionnel ou d'acceptation (c'est-à-dire de l'ensemble du système). Cependant, les codes ont souvent des étapes distinctes (ou peuvent être divisés en eux). par exemple advection, pression, viscosité. On ne pouvait alors tester qu'une seule de ces étapes (une "unité"). De même, un code pourrait être testé sans BC, puis avec un. Un ami a fait son doctorat sur les tests unitaires, et il a estimé que le plus grand avantage était que cela vous obligeait à diviser votre programme en unités, de sorte qu'il peut être testé par unité ... peut-être que cela est plus applicable ici qu'il n'y paraît au début (et par d'autres moyens que je ne connais pas).
hyperpallium
6

Bill a déjà énuméré quelques méthodes qui répondent à vos préoccupations.

Pour répondre à votre troisième point, non, il n'y a aucune raison d'introduire un couplage solide entre les pièces. Bien au contraire: si vos fonctions ou classes ont des interfaces bien définies, il sera beaucoup plus facile d'échanger par exemple un solveur linéaire contre un autre, ou un schéma pas à pas. Il suffit de lui résister et vous pourrez ensuite tester ces composants séparément. Nous l'avons fait avec deal.II pendant des décennies.

À votre quatrième point: si votre méthode est une expérience, vos expériences avec la méthode constituent un test. Tant que vous n'aurez pas d'analyse, vous devrez prendre ces résultats de test le mieux possible. Mais généralement, vous avez une attente par exemple pour l'ordre d'une méthode, ou vous savez qu'elle est exacte pour une certaine classe de solutions, par exemple des polynômes jusqu'à un certain degré. Leur vérification devrait faire partie de vos expériences et, à mesure que l'analyse s'améliore, des tests peuvent être ajoutés.

Guido Kanschat
la source
1
Pour ajouter à la réponse de Guido, l'expérience dont il parle est encodée dans les ~ 3 000 tests que nous exécutons sur deal.II après chaque changement: dealii.org/developer/development/… . Sur la question de savoir quoi faire si vous ne connaissez pas la réponse exacte: écrivez quand même un test et laissez-le comparer la réponse d'aujourd'hui à la réponse d'hier (ou chaque fois que vous avez écrit le test). Avoir un moyen de repérer les changements dans la sortie d'un code est précieux même si vous ne savez pas s'ils ont rendu la réponse incorrecte ou corrigé une réponse précédemment incorrecte.
Wolfgang Bangerth,
3

J'ai récemment trouvé cette thèse sur TDD en science informatique. Je ne l'ai pas encore lu, donc je ne sais pas si c'est bon, mais j'espère que cela peut être utile.

http://cyber.ua.edu/files/2014/12/u0015_0000001_0001551.pdf

mags
la source
1
J'ai survolé une partie de l'intro et des conclusions, et en supposant un niveau de qualité comparable à la thèse de doctorat standard, cela explique à la fois le processus (d'une manière de haut niveau) et donne des mesures réelles quant à son efficacité. Je pense que c'est tout à fait une trouvaille.
Godric Seer
Le lien est mort. Vouliez-vous dire: Nanthaamornphong, A. «L'efficacité des techniques de développement et de refactoring pilotées par les tests dans le développement de logiciels de science informatique et d'ingénierie». PhD diss., Uni. Alabama (2014).
AlQuemist