Développement F # et tests unitaires?

107

Je viens de commencer avec F #, qui est mon premier langage fonctionnel. Je travaille quasi exclusivement avec C # et j'apprécie beaucoup la façon dont F # m'amène à repenser ma façon d'écrire du code. Un aspect que je trouve un peu désorientant est le changement dans le processus d'écriture du code. J'utilise TDD depuis des années en C # maintenant, et j'apprécie vraiment d'avoir des tests unitaires pour savoir où j'en suis.

Jusqu'à présent, mon processus avec F # a été d'écrire certaines fonctions, de jouer avec elles avec la console interactive jusqu'à ce que je sois "raisonnablement" sûr qu'elles fonctionnent, et de les modifier et les combiner. Cela fonctionne bien sur des problèmes à petite échelle comme le projet Euler, mais je ne peux pas imaginer construire quelque chose de grand de cette façon.

Comment les gens abordent-ils les tests unitaires et construisent une suite de tests pour un programme F #? Existe-t-il un équivalent au TDD? Tous les conseils ou pensées sont appréciés.

Mathias
la source
1
expert-fsharp.com/CodeSamples/Forms/… montre un exemple simple d'utilisation de NUnit avec F #.
itowlson
Related : stackoverflow.com/questions/5667372/… (Unquote est bien plus qu'une note de bas de page / commentaire car il est dans la réponse sur cette page)
Ruben Bartelink
Une chose qui manque dans ces réponses est un bon exemple de Foq, AutoFixture.AutoFoq et AutoFixture.xUnit alliés à l'inférence de type F #. Voir trelford.com/blog/post/test5.aspx et trelford.com/blog/post/fstestlang.aspx pour un avant - goût et un jour j'écrirai une réponse correcte ici
Ruben Bartelink

Réponses:

77

Les développeurs pilotés par les tests doivent se sentir comme chez eux dans des langages fonctionnels comme F #: les petites fonctions qui donnent des résultats répétables de manière déterministe se prêtent parfaitement aux tests unitaires. Il existe également des fonctionnalités dans le langage F # qui facilitent l'écriture des tests. Prenez, par exemple, les expressions d'objets . Vous pouvez très facilement écrire des faux pour les fonctions qui prennent comme entrée un type d'interface.

Si quoi que ce soit, F # est un langage orienté objet de première classe et vous pouvez utiliser les mêmes outils et astuces que vous utilisez lorsque vous faites TDD en C #. Il existe également des outils de test écrits ou spécifiquement pour F #:

Matthew Podwysocki a écrit une grande série sur les tests unitaires dans les langages fonctionnels. L'oncle Bob a également écrit un article stimulant ici .

Ray Vernagus
la source
9
J'ai également développé (et je suis en train de développer activement) une bibliothèque de tests unitaires spécifique à F # appelée Unquote: code.google.com/p/unquote . Il vous permet d'écrire des assertions de test sous forme d'expressions booléennes F # vérifiées statiquement à l'aide de citations F # et produit automatiquement de jolis messages d'échec de test. Il fonctionne sans configuration avec un support spécial pour xUnit.net et NUnit et prend généralement en charge tout cadre de test unitaire basé sur des exceptions. Il fonctionne même dans les sessions FSI, permettant une migration transparente des tests interactifs vers les suites de tests formelles.
Stephen Swensen
Il y a aussi Pex , même si c'est un peu plus difficile à faire.
Benjol
1
Oncle Bob Link semble mort
Aage
22

J'utilise NUnit, et cela ne me semble pas aussi difficile à lire ni à écrire:

open NUnit.Framework

[<TestFixture>]
type myFixture() = class

    [<Test>]
    member self.myTest() =
       //test code

end

Comme mon code est un mélange de F # et d'autres langages .Net, j'aime le fait que j'écris les tests unitaires de la même manière et avec une syntaxe similaire en F # et C #.

David Glaubman
la source
4
Après avoir lu d'autres réponses ici, j'ai essayé FSUnit, et je pense que c'est génial. Il fonctionne bien avec TestDriven.Net (tout comme NUnit), encourage un style fluide d'écriture de tests auto-documentés et, comme Ray déclare, est "plus à l'aise dans les langages F #". Pas mal pour 21 lignes de code! (Et quelques recommandations de mise en page / nom). Deux remarques rapides: 1. La DLL FSUnit précompilée ne fonctionnait pas pour moi. La construction à partir des sources (FsUnit.NUnit-0.9.0.fs) a résolu le problème. 2. TestDriven.Net ne reconnaît pas les noms TextFixture qui semblent `like this`. Les noms de test utilisant un formulaire à double coche sont reconnus.
David Glaubman
15

Jetez un œil à FsCheck , un outil de test automatique pour F #, est essentiellement un portage de QuickCheck de Haskell. Il vous permet de fournir une spécification du programme, sous la forme de propriétés que les fonctions ou méthodes doivent satisfaire, et FsCheck teste que les propriétés tiennent dans un grand nombre de cas générés aléatoirement.

Page FsCheck CodePlex

Page Auteur FsCheck

primodème
la source
Oui, je pense que FsCheck offre beaucoup plus que les frameworks de tests unitaires traditionnels tels que NUnit etc.
Robert
11

Comme le suggère dglaubman, vous pouvez utiliser NUnit. xUnit.net fournit également un support pour cela et fonctionne bien avec TestDriven.net . Le code ressemble aux tests NUnit mais sans la nécessité d'encapsuler le test dans un type conteneur.

#light

// Supply a module name here not a combination of module and namespace, otherwise
// F# cannot resolve individual tests nfrom the UI.
module NBody.DomainModel.FSharp.Tests

open System
open Xunit

open Internal

[<Fact>]
let CreateOctantBoundaryReordersMinMax() =
    let Max = VectorFloat(1.0, 1.0, 1.0)
    let Min = VectorFloat(-1.0, -1.0, -1.0)

    let result = OctantBoundary.create Min Max

    Assert.Equal(Min, result.Min)     
    Assert.Equal(Max, result.Max) 
Ade Miller
la source
Depuis la 1.9.1, les nouvelles surcharges de Xunit semblent causer des ravages avec mon F #.
Rick Minerich
@RickMinerich J'ai vécu la même chose avec mon code. J'ai juste fini par ajouter des annotations de type explicite afin que la surcharge correcte soit choisie. Toutefois, cela n'ajoute plus de bruit à votre code malheureusement.
Erik Schierboom
11

Je pense que c'est une question très intéressante sur laquelle je me suis beaucoup posé moi-même. Mes pensées jusqu'à présent ne sont que des pensées, alors prenez-les pour ce qu'elles sont.

Je pense que le filet de sécurité d'une suite de tests automatisés est un atout trop précieux pour être abandonné, aussi séduisante que puisse être cette console interactive, je prévois donc de continuer à écrire des tests unitaires comme je l'ai toujours fait.

L'une des principales forces de .NET réside dans les capacités multilingues. Je sais que je vais bientôt écrire du code de production F #, mais mon plan est d'écrire des tests unitaires en C # pour faciliter mon chemin dans ce qui est pour moi un nouveau langage. De cette façon, je peux également tester que ce que j'écris en F # sera compatible avec C # (et d'autres langages .NET).

Avec cette approche, je comprends qu'il existe certaines fonctionnalités de F # que je ne peux utiliser qu'en interne dans mon code F #, mais que je ne peux pas exposer dans le cadre de mon API publique, mais j'accepterai cela, tout comme j'accepte aujourd'hui qu'il y a certaines choses C # me permet d'exprimer (comme uint) qui ne sont pas conformes à CLS, et je m'abstiens donc de les utiliser.

Mark Seemann
la source
2
Comment allait ton plan? C'était facile de tester du code f # avec du code c #? J'ai commencé à apprendre f # et mon plan est d'écrire une partie de mon projet en f # et j'ai la même idée: écrire des tests unitaires en c # pour f # aussi.
Peter Porfy
@Mark des mises à jour? J'ai également du mal à entrer dans le flux en utilisant TDD avec F #.
Scott Nimrod
1
@ScottNimrod Quelques mises à jour: quatre de mes cours Pluralsight portent sur les tests ou TDD avec F #. Vous trouverez également de nombreux enregistrements gratuits de conférences sur mon profil Lanyrd . Enfin, il y a mon blog .
Mark Seemann
3
@ScottNimrod Je ne peux pas recommander de prendre le temps et / ou de payer suffisamment pour voir l'ensemble complet des cours PS de Mark - cela va tout mettre en place dans votre tête avec une efficacité maximale. Bien que cela puisse s'appliquer ou non à vos besoins spécifiques, «Une architecture fonctionnelle en F #» relie également de nombreux points et devrait également être fortement pris en compte.
Ruben Bartelink
7

Vous pouvez jeter un œil à FSUnit - même si je ne l'ai pas encore utilisé, cela vaut peut-être la peine d'essayer. Certainement mieux que d'utiliser par exemple NUnit (natif) en F #.

ShdNx
la source
1
ShdNx, pourquoi recommanderiez-vous contre NUnit? Le livre F # de Don Syme montre NUnit pour les tests et il ressemble assez à l'utilisation de NUnit en C #. Le DSL FSUnit a l'air cool, mais pour les personnes déjà familiarisées avec NUnit (Mathias "utilise TDD depuis des années"), est-ce votre expérience que l'utilisation de NUnit avec F # est plus problématique qu'avec C # ou VB?
itowlson
J'appuie le commentaire et la question itowlson. Très certainement, NUnit en F # semble assez étrange, mais à part cela, êtes-vous conscient de problèmes spécifiques qui en font une bonne idée d'utiliser autre chose?
Mathias
1
Je dirais que "paraître assez étrange" est généralement une raison impérieuse de trouver quelque chose de mieux. Paraître étrange signifie difficile à lire, et difficile à lire signifie des bogues. (Je suppose que "paraître étrange" est complètement différent de "paraître nouveau et / ou inconnu" - inconnu deviendra familier, impair restera étrange.)
James Moore
1
Pour être honnête (comme je l'ai mentionné dans ma réponse), je n'ai pas encore utilisé FSUnit, mais j'ai lu qu'il était très pénible d'utiliser NUnit en F #. Désolé si ce n'est pas le cas.
ShdNx
4
Pour être clair, vous aurez toujours des membres TestFixtures et Test si vous utilisez FsUnit. Ce que vous n'aurez pas, ce sont les appels Assert.X standard. FsUnit vous donne simplement un wrapper autour de cette partie de NUnit qui le rend plus à l'aise dans le langage F #.
Ray Vernagus
1

Bien que je sois un peu en retard à la fête, j'aimerais souhaiter la bienvenue à Mathias en F # (mieux vaut tard que jamais;)) et carillon que vous aimerez peut-être ma bibliothèque de tests unitaires, Expecto

Expecto a quelques fonctionnalités qui pourraient vous plaire:

  • Syntaxe F # partout, teste comme valeurs; écrire un simple F # pour générer des tests
  • Utilisez le module Expect intégré ou une bibliothèque externe comme Unquote pour les assertions
  • Tests parallèles par défaut
  • Testez votre code Hopac ou votre code Async; Expecto est asynchrone partout
  • Journalisation et métriques enfichables via Logary Facade; écrivez facilement des adaptateurs pour les systèmes de construction ou utilisez le mécanisme de synchronisation pour créer un tableau de bord InfluxDB + Grafana des temps d'exécution de vos tests
  • Prise en charge intégrée de BenchmarkDotNet
  • Prise en charge intégrée de FsCheck; facilite la construction de tests avec des données générées / aléatoires ou la construction de modèles invariants de l'espace d'états de votre objet / acteur

-

open Expecto

let tests =
  test "A simple test" {
    let subject = "Hello World"
    Expect.equal subject "Hello World" "The strings should equal"
  }

[<EntryPoint>]
let main args =
  runTestsWithArgs defaultConfig args tests

https://github.com/haf/expecto/

Henrik
la source