Programowanie F # i testy jednostkowe?

107

Właśnie zacząłem z F #, który jest moim pierwszym językiem funkcjonalnym. Pracowałem prawie wyłącznie z językiem C # i bardzo się cieszę, jak F # prowadzi mnie do ponownego przemyślenia sposobu, w jaki piszę kod. Jeden aspekt, który uważam za nieco dezorientujący, to zmiana w procesie pisania kodu. Od lat używam TDD w C # i naprawdę doceniam, że mam testy jednostkowe, aby wiedzieć, gdzie jestem.

Do tej pory mój proces z F # polegał na pisaniu niektórych funkcji, bawieniu się nimi za pomocą konsoli interaktywnej, dopóki nie będę „w miarę” pewny, że działają, a także poprawianiu i łączeniu. Działa to dobrze w przypadku problemów na małą skalę, takich jak projekt Euler, ale nie wyobrażam sobie zbudowania w ten sposób czegoś dużego.

Jak ludzie podchodzą do testowania jednostkowego i tworzenia zestawu testów dla programu F #? Czy istnieje odpowiednik TDD? Wszelkie wskazówki lub myśli są mile widziane.

Mathias
źródło
1
expert-fsharp.com/CodeSamples/Forms/ ... przedstawia prosty przykład użycia NUnit z F #.
itowlson
patrz stackoverflow.com/questions/1468772/…
Mauricio Scheffer
related: stackoverflow.com/questions/5667372/… (Cofnij cytat to znacznie więcej niż przypis / komentarz, ponieważ znajduje się w zestawie odpowiedzi na tej stronie)
Ruben Bartelink
W tych odpowiedziach brakuje tylko odpowiedniego przykładu Foq, AutoFixture.AutoFoq i AutoFixture.xUnit powiązanych z wnioskiem F # o typie. Zobacz trelford.com/blog/post/test5.aspx i trelford.com/blog/post/fstestlang.aspx po degustację, a pewnego dnia napiszę tutaj poprawną odpowiedź
Ruben Bartelink

Odpowiedzi:

77

Programiści sterowani testami powinni czuć się jak w domu w językach funkcjonalnych, takich jak F #: małe funkcje, które dają deterministycznie powtarzalne wyniki, doskonale nadają się do testów jednostkowych. Istnieją również możliwości w języku F # ułatwiające pisanie testów. Weźmy na przykład wyrażenia obiektu . Możesz bardzo łatwo napisać podróbki dla funkcji, które jako dane wejściowe przyjmują typ interfejsu.

Jeśli już, F # jest pierwszorzędnym językiem obiektowym i możesz używać tych samych narzędzi i sztuczek, których używasz podczas wykonywania TDD w C #. Istnieją również narzędzia do testowania napisane w języku F # lub specjalnie dla niego:

Matthew Podwysocki napisał świetną serię na temat testów jednostkowych w językach funkcjonalnych. Wujek Bob napisał również inspirującej artykuł tutaj .

Ray Vernagus
źródło
9
Opracowałem również (i aktywnie rozwijam) bibliotekę testów jednostkowych specyficznych dla języka F # o nazwie Unquote: code.google.com/p/unquote . Pozwala na pisanie asercji testowych jako zwykłych, statycznie sprawdzanych wyrażeń boolowskich F # przy użyciu F # cudzysłowów i automatycznie generuje ładne komunikaty o niepowodzeniach testu. Działa bez konfiguracji ze specjalną obsługą zarówno xUnit.net, jak i NUnit i ogólnie obsługuje wszelkie ramy testów jednostkowych oparte na wyjątkach. Działa nawet w sesjach FSI, umożliwiając bezproblemową migrację z testów interaktywnych do formalnych zestawów testów.
Stephen Swensen
Jest też Pex , choć trochę trudniej to zrozumieć.
Benjol
1
Wujek Bob link wydaje się martwy
Aage
22

Używam NUnit i nie wydaje mi się to tak trudne do odczytania ani uciążliwe w pisaniu:

open NUnit.Framework

[<TestFixture>]
type myFixture() = class

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

end

Ponieważ mój kod jest mieszanką języka F # i innych języków .Net, podoba mi się fakt, że testy jednostkowe piszę w zasadzie w ten sam sposób iz podobną składnią w językach F # i C #.

David Glaubman
źródło
4
Po przeczytaniu innych odpowiedzi tutaj, spróbowałem FSUnit i myślę, że jest świetny. Działa dobrze z TestDriven.Net (podobnie jak NUnit), zachęca do płynnego stylu pisania testów samodokumentujących i, jak twierdzi Ray, jest „bardziej zadowalający w językach F #”. Nieźle jak na 21 linii kodu! (I kilka zaleceń dotyczących układu / nazewnictwa). Dwie krótkie uwagi: 1. Prekompilowana biblioteka DLL FSUnit nie działa dla mnie. Kompilowanie ze źródła (FsUnit.NUnit-0.9.0.fs) rozwiązało problem. 2. TestDriven.Net nie rozpoznaje wyglądających nazw TextFixture `like this`. Nazwy testów z podwójnym zaznaczeniem są rozpoznawane.
David Glaubman,
15

Spójrz na FsCheck , automatyczne narzędzie testujące dla F #, jest w zasadzie portem QuickCheck firmy Haskell. Pozwala na podanie specyfikacji programu w postaci właściwości, które powinny spełniać funkcje lub metody, a FsCheck sprawdza, czy właściwości te posiadają w dużej liczbie losowo generowanych przypadków.

Strona FsCheck CodePlex

Strona autora programu FsCheck

primodemus
źródło
Tak, myślę, że FsCheck oferuje znacznie więcej niż tradycyjne frameworki do testów jednostkowych, takie jak NUnit itp.
Robert
11

Jak sugeruje dglaubman, możesz użyć NUnit. xUnit.net zapewnia również wsparcie dla tego i dobrze współpracuje z TestDriven.net . Kod wygląda podobnie do testów NUnit, ale bez wymogu opakowywania testu w typ zawierający.

#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
źródło
Od wersji 1.9.1 nowe przeciążenia Xunit wydają się powodować spustoszenie w moim F #.
Rick Minerich
@RickMinerich Doświadczyłem tego samego, co dzieje się z moim kodem. Po prostu dodałem wyraźne adnotacje typu, aby wybrać poprawne przeciążenie. Jednak powoduje to niestety więcej szumów w kodzie.
Erik Schierboom
11

Myślę, że to bardzo interesujące pytanie, nad którym sam bardzo się zastanawiałem. Moje myśli to tylko myśli, więc weź je za to, czym są.

Myślę, że sieć bezpieczeństwa zestawu testów automatycznych jest zbyt cennym atutem, aby go odpuścić, jakkolwiek pociągająca może być ta konsola interaktywna, więc planuję kontynuować pisanie testów jednostkowych, tak jak zawsze.

Jedną z głównych zalet platformy .NET są możliwości obsługi wielu języków. Wiem, że wkrótce będę pisać kod produkcyjny w języku F #, ale planuję napisać testy jednostkowe w języku C #, aby ułatwić sobie drogę do tego, co jest dla mnie nowym językiem. W ten sposób mogę również sprawdzić, czy to, co piszę w F #, będzie kompatybilne z C # (i innymi językami .NET).

Dzięki takiemu podejściu rozumiem, że istnieją pewne funkcje języka F #, których mogę używać tylko wewnętrznie w moim kodzie F #, ale nie mogę ich udostępniać w ramach mojego publicznego interfejsu API, ale zaakceptuję to, tak jak akceptuję dzisiaj, że są pewne rzeczy C # pozwala mi wyrazić (lubić uint), które nie są zgodne z CLS, więc powstrzymuję się od ich używania.

Mark Seemann
źródło
2
Jak szedł twój plan? Czy łatwo było przetestować kod F # za pomocą kodu C #? Zacząłem uczyć się f # i planuję napisać część projektu w języku f # i mam ten sam pomysł: napisać testy jednostkowe w języku c # również dla języka f #.
Peter Porfy,
@Znakować jakieś aktualizacje? Trudno mi też wejść w flow używając TDD z F #.
Scott Nimrod,
1
@ScottNimrod Całkiem kilka aktualizacji: cztery z moich kursów Pluralsight dotyczą testowania lub TDD z F #. Na moim profilu Lanyrda znajdziesz także wiele bezpłatnych nagrań rozmów konferencyjnych . Wreszcie jest mój blog .
Mark Seemann
3
@ScottNimrod Nie mogę polecić poświęcenia czasu i / lub zapłacenia pieniędzy, aby wystarczająco dobrze obejrzeć cały zestaw kursów PS Marka - wszystko to połączy w twojej głowie z maksymalną wydajnością. Chociaż może, ale nie musi, odnosić się do twoich konkretnych potrzeb, „Architektura funkcjonalna w języku F #” również łączy wiele kropek i również powinna być mocno rozważona.
Ruben Bartelink
7

Możesz rzucić okiem na FSUnit - chociaż jeszcze go nie używałem, może warto spróbować. Z pewnością lepsze niż używanie na przykład (natywnego) NUnit w F #.

ShdNx
źródło
1
ShdNx, dlaczego poleciłbyś NUnit? Książka Dona Syme F # pokazuje NUnit do testowania i wygląda bardzo podobnie do używania NUnit w C #. FSUnit DSL wygląda fajnie, ale dla osób już zaznajomionych z NUnit (Mathias „używa TDD od lat”) czy z doświadczenia wynika, że ​​używanie NUnit z F # jest bardziej problematyczne niż z C # lub VB?
itowlson
Drugi komentarz itowlson i pytanie. Zdecydowanie NUnit w F # wygląda dość dziwnie, ale poza tym, czy jesteś świadomy konkretnych problemów, które sprawiają, że dobrym pomysłem jest użycie czegoś innego?
Mathias
1
Powiedziałbym, że „wyglądanie całkiem dziwnie” jest zwykle nieodpartym powodem, aby znaleźć coś lepszego. Dziwny wygląd oznacza trudny do odczytania, a trudny do odczytania oznacza błędy. (Zakładam, że „dziwny wygląd” jest zupełnie inny niż „wyglądanie na nowego i / lub nieznajomego” - nieznany stanie się znajomy, dziwny pozostanie dziwny.)
James Moore
1
Szczerze mówiąc (jak wspomniałem w mojej odpowiedzi) jeszcze nie korzystałem z FSUnit, ale czytałem, że bardzo bolesne jest używanie NUnit w F #. Przepraszam, jeśli tak nie jest.
ShdNx
4
Aby było jasne, nadal będziesz mieć członków TestFixtures i Test, jeśli używasz FsUnit. To, czego nie będziesz mieć, to standardowe wywołania Assert.X. FsUnit po prostu zapewnia otokę wokół tej części NUnit, dzięki czemu jest bardziej komfortowa w języku F #.
Ray Vernagus
1

Mimo, że jestem trochę spóźniony na imprezę, chciałbym przywitać Mathiasa na F # (lepiej późno niż wcale;)) i dodać, że spodobałaby Ci się moja biblioteka testów jednostkowych Expecto

Expecto ma kilka funkcji, które mogą Ci się spodobać:

  • Składnia F # w całym tekście, testy jako wartości; napisz zwykły F #, aby wygenerować testy
  • Użyj wbudowanego modułu Expect lub biblioteki zewnętrznej, takiej jak Unquote, do potwierdzeń
  • Testy równoległe domyślnie
  • Przetestuj swój kod Hopac lub kod Async; Expecto jest asynchroniczne przez cały czas
  • Podłączane rejestrowanie i metryki za pośrednictwem Logary Facade; łatwo pisz adaptery do systemów kompilacji lub korzystaj z mechanizmu czasowego do budowania pulpitu InfluxDB + Grafana z czasami wykonywania testów
  • Wbudowana obsługa BenchmarkDotNet
  • Wbuduj obsługę FsCheck; ułatwia tworzenie testów z wygenerowanymi / losowymi danymi lub budowanie niezmiennych modeli przestrzeni stanów obiektu / aktora

-

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
źródło