F # Entwicklung und Unit Testing?

107

Ich habe gerade mit F # angefangen, meiner ersten funktionalen Sprache. Ich habe quasi ausschließlich mit C # gearbeitet und genieße es sehr, wie F # mich dazu bringt, meine Schreibweise für Code zu überdenken. Ein Aspekt, den ich etwas desorientiert finde, ist die Änderung beim Schreiben von Code. Ich benutze TDD seit Jahren in C # und freue mich sehr über Unit-Tests, um zu wissen, wo ich mich befinde.

Bisher bestand mein Prozess mit F # darin, einige Funktionen zu schreiben, mit ihnen mit der interaktiven Konsole zu spielen, bis ich "ziemlich" sicher bin, dass sie funktionieren, und zu optimieren und zu kombinieren. Dies funktioniert gut bei kleinen Problemen wie dem Euler-Projekt, aber ich kann mir nicht vorstellen, auf diese Weise etwas Großes zu bauen.

Wie gehen Menschen mit Unit-Tests um und erstellen eine Testsuite für ein F # -Programm? Gibt es ein Äquivalent zu TDD? Hinweise oder Gedanken sind willkommen.

Mathias
quelle
1
Expert-fsharp.com/CodeSamples/Forms/… zeigt ein einfaches Beispiel für die Verwendung von NUnit mit F #.
Itowlson
siehe stackoverflow.com/questions/1468772/…
Mauricio Scheffer
verwandt: stackoverflow.com/questions/5667372/… (Unquote ist weit mehr als eine Fußnote / ein Kommentar, da es im Antwortsatz auf dieser Seite enthalten ist)
Ruben Bartelink
Eine Sache, die in diesen Antworten fehlt, ist ein geeignetes Beispiel für Foq, AutoFixture.AutoFoq und AutoFixture.xUnit, die mit der Typinferenz von F # verbunden sind. Siehe trelford.com/blog/post/test5.aspx und trelford.com/blog/post/fstestlang.aspx für einen Vorgeschmack und eines Tages werde ich hier eine richtige Antwort schreiben
Ruben Bartelink

Antworten:

77

Testgetriebene Entwickler sollten sich in funktionalen Sprachen wie F # wie zu Hause fühlen: Kleine Funktionen, die deterministisch wiederholbare Ergebnisse liefern, eignen sich perfekt für Unit-Tests. Es gibt auch Funktionen in der F # -Sprache, die das Schreiben von Tests erleichtern. Nehmen Sie zum Beispiel Objektausdrücke . Sie können sehr einfach Fälschungen für Funktionen schreiben, deren Eingabe ein Schnittstellentyp ist.

Wenn überhaupt, ist F # eine erstklassige objektorientierte Sprache, und Sie können dieselben Tools und Tricks verwenden, die Sie beim Ausführen von TDD in C # verwenden. Es gibt auch einige Testtools, die in oder speziell für F # geschrieben wurden:

Matthew Podwysocki hat eine großartige Serie über Unit-Tests in funktionalen Sprachen geschrieben. Uncle Bob schrieb auch einen Gedanken Artikel zu provozieren hier .

Ray Vernagus
quelle
9
Ich habe auch eine F # -spezifische Unit-Testing-Bibliothek namens Unquote entwickelt (und entwickle sie aktiv): code.google.com/p/unquote . Es ermöglicht Ihnen, Testzusicherungen als einfache, statisch überprüfte F # -Boolesche Ausdrücke unter Verwendung von F # -Zitaten zu schreiben, und erzeugt automatisch nette Testfehlermeldungen. Es funktioniert konfigurationsfrei mit spezieller Unterstützung für xUnit.net und NUnit und unterstützt im Allgemeinen jedes ausnahmebasierte Unit-Testing-Framework. Es funktioniert sogar in FSI-Sitzungen und ermöglicht eine nahtlose Migration von interaktiven Tests zu formalen Testsuiten.
Stephen Swensen
Es gibt auch Pex , obwohl das etwas schwieriger zu verstehen ist.
Benjol
1
Onkel Bob Link scheint tot zu sein
Aage
22

Ich benutze NUnit und es fällt mir nicht so schwer zu lesen oder schwer zu schreiben:

open NUnit.Framework

[<TestFixture>]
type myFixture() = class

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

end

Da mein Code eine Mischung aus F # und anderen .Net-Sprachen ist, gefällt mir die Tatsache, dass ich die Unit-Tests im Grunde auf die gleiche Weise und mit ähnlicher Syntax sowohl in F # als auch in C # schreibe.

David Glaubman
quelle
4
Nachdem ich andere Antworten hier gelesen hatte, habe ich FSUnit ausprobiert und finde es großartig. Es funktioniert gut mit TestDriven.Net (wie auch NUnit), fördert einen flüssigen Stil beim Schreiben von selbstdokumentierenden Tests und ist, wie Ray feststellt, "in den F # -Sprachen mehr zu Hause". Nicht schlecht für 21 Codezeilen! (Und ein paar Layout- / Namensempfehlungen). Zwei kurze Hinweise: 1. Die vorkompilierte FSUnit-DLL hat bei mir nicht funktioniert. Das Erstellen aus der Quelle (FsUnit.NUnit-0.9.0.fs) hat das Problem behoben. 2. TestDriven.Net erkennt keine aussehenden TextFixture-Namen `like this`. Testnamen mit Double-Tick-Form werden erkannt.
David Glaubman
15

Werfen Sie einen Blick auf FsCheck , ein automatisches Testwerkzeug für F #, ist im Grunde eine Portierung von Haskells QuickCheck. Sie können eine Spezifikation des Programms in Form von Eigenschaften bereitstellen, die die Funktionen oder Methoden erfüllen sollten, und FsCheck testet, ob die Eigenschaften in einer großen Anzahl zufällig generierter Fälle enthalten sind.

FsCheck CodePlex Page

FsCheck Author Page

Primodemus
quelle
Ja, ich denke, FsCheck bietet viel mehr als herkömmliche Unit-Testing-Frameworks wie NUnit usw.
Robert
11

Wie dglaubman vorschlägt, können Sie NUnit verwenden. xUnit.net bietet auch Unterstützung dafür und funktioniert gut mit TestDriven.net . Der Code ähnelt NUnit-Tests, ohne dass der Test in einen enthaltenen Typ eingeschlossen werden muss.

#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
quelle
Ab 1.9.1 scheinen die neuen Überladungen von Xunit mit meiner F # Chaos zu verursachen.
Rick Minerich
@ RickMinerich Ich habe das gleiche mit meinem Code erlebt. Am Ende habe ich nur explizite Typanmerkungen hinzugefügt, damit die richtige Überladung ausgewählt wird. Dies ist jedoch nicht leider mehr Lärm zu Ihrem Code hinzuzufügen.
Erik Schierboom
11

Ich denke, das ist eine sehr interessante Frage, über die ich mich selbst viel gewundert habe. Meine Gedanken sind bisher nur Gedanken, also nimm sie als das, was sie sind.

Ich denke, dass das Sicherheitsnetz einer automatisierten Testsuite zu wertvoll ist, um loszulassen, so verlockend diese interaktive Konsole auch sein mag, daher plane ich, weiterhin Unit-Tests zu schreiben, wie ich es immer getan habe.

Eine der Hauptstärken von .NET sind die sprachübergreifenden Funktionen. Ich weiß, dass ich bald F # -Produktionscode schreiben werde, aber mein Plan ist es, Unit-Tests in C # zu schreiben, um mir den Weg in eine für mich neue Sprache zu erleichtern. Auf diese Weise kann ich auch testen, ob das, was ich in F # schreibe, mit C # (und anderen .NET-Sprachen) kompatibel ist.

Mit diesem Ansatz verstehe ich, dass es bestimmte Funktionen von F # gibt, die ich nur intern in meinem F # -Code verwenden kann, aber nicht als Teil meiner öffentlichen API verfügbar mache, aber ich werde das akzeptieren, genauso wie ich heute akzeptiere, dass es bestimmte Dinge gibt Mit C # kann ich ausdrücken (wie uint), die nicht CLS-kompatibel sind, und daher verzichte ich darauf, sie zu verwenden.

Mark Seemann
quelle
2
Wie war dein Plan? Es war einfach, f # -Code mit c # -Code zu testen? Ich habe angefangen, f # zu lernen und mein Plan ist es, einen Teil meines Projekts in f # zu schreiben, und ich habe die gleiche Idee: Unit-Tests in c # auch für f # zu schreiben.
Peter Porfy
@ Mark irgendwelche Updates? Es fällt mir auch schwer, mit TDD mit F # in Fluss zu kommen.
Scott Nimrod
1
@ScottNimrod Einige Updates: In vier meiner Pluralsight-Kurse geht es um das Testen oder TDD mit F #. Auf meinem Lanyrd-Profil finden Sie auch viele kostenlose Aufzeichnungen von Konferenzgesprächen . Endlich gibt es meinen Blog .
Mark Seemann
3
@ScottNimrod Ich kann nicht empfehlen, sich die Zeit zu nehmen und / oder das Geld zu bezahlen, um den gesamten Satz von Marks PS-Kursen hoch genug anzusehen - es wird alles mit maximaler Effizienz in Ihrem Kopf zusammenschnappen. Während es möglicherweise auf Ihre spezifischen Anforderungen zutrifft oder nicht, verbindet die "A Functional Architecture in F #" auch viele Punkte und sollte ebenfalls stark berücksichtigt werden.
Ruben Bartelink
7

Sie könnten einen Blick auf FSUnit werfen - obwohl ich es noch nicht benutzt habe, könnte es einen Versuch wert sein. Sicher besser als zum Beispiel (native) NUnit in F # zu verwenden.

ShdNx
quelle
1
ShdNx, warum würden Sie gegen NUnit empfehlen? Don Symes F # -Buch zeigt NUnit zum Testen und es sieht ziemlich ähnlich aus wie NUnit in C #. Das FSUnit DSL sieht cool aus, aber für Leute, die bereits mit NUnit vertraut sind (Mathias verwendet "TDD seit Jahren"), ist es Ihrer Erfahrung nach problematischer als mit C # oder VB, NUnit mit F # zu verwenden?
Itowlson
Ich zweite itowlson Kommentar und Frage. Auf jeden Fall sieht NUnit in F # ziemlich seltsam aus, aber sind Ihnen außerdem bestimmte Probleme bekannt, die es zu einer guten Idee machen, etwas anderes zu verwenden?
Mathias
1
Ich würde sagen, dass "ziemlich seltsam aussehen" normalerweise ein zwingender Grund ist, etwas Besseres zu finden. Seltsam auszusehen bedeutet schwer zu lesen, und schwer zu lesen bedeutet Fehler. (Ich gehe davon aus, dass "seltsam aussehen" völlig anders ist als "neu und / oder unbekannt aussehen" - unbekannt wird vertraut, seltsam wird seltsam bleiben.)
James Moore
1
Um ehrlich zu sein (wie ich in meiner Antwort erwähnt habe), habe ich FSUnit noch nicht verwendet, aber ich habe gelesen, dass es sehr schmerzhaft ist, NUnit in F # zu verwenden. Entschuldigung, wenn es nicht so ist.
ShdNx
4
Um klar zu sein, haben Sie weiterhin TestFixtures und Test-Mitglieder, wenn Sie FsUnit verwenden. Was Sie nicht haben werden, sind die Standard-Assert.X-Aufrufe. FsUnit gibt Ihnen einfach einen Wrapper um diesen Teil von NUnit, der es in der F # -Sprache besser zu Hause macht.
Ray Vernagus
1

Obwohl ich ein bisschen zu spät zur Party komme, würde ich Mathias gerne in F # begrüßen (besser spät als nie;)) und mich darauf einlassen, dass Ihnen Expecto gefallen könnte

Expecto bietet einige Funktionen, die Ihnen gefallen könnten:

  • F # -Syntax durchgehend, Tests als Werte; Schreiben Sie einfach F #, um Tests zu generieren
  • Verwenden Sie das integrierte Expect-Modul oder eine externe Bibliothek wie Unquote für Zusicherungen
  • Standardmäßig parallele Tests
  • Testen Sie Ihren Hopac-Code oder Ihren Async-Code. Expecto ist durchweg asynchron
  • Steckbare Protokollierung und Metriken über Logary Facade; Schreiben Sie einfach Adapter für Build-Systeme oder verwenden Sie den Timing-Mechanismus zum Erstellen eines InfluxDB + Grafana-Dashboards für die Ausführungszeiten Ihrer Tests
  • Integrierte Unterstützung für BenchmarkDotNet
  • Integrierte Unterstützung für FsCheck; macht es einfach, Tests mit generierten / zufälligen Daten zu erstellen oder invariante Modelle des Zustandsraums Ihres Objekts / Akteurs zu erstellen

- -

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
quelle