Już od jakiegoś czasu miałem oko na spróbowanie F#. Połączenie szybkiego programowania funkcyjnego wraz z potężną biblioteką .NET brzmi bardzo fajnie i takie jest w rzeczywistości.
Zobaczyłem, że NUnit ma w swoich przykładach projekt w F#, więc postanowiłem przepisać moje obecne testy na F#. Nie przewidziałem jednak trudności wynikających z nieznajomości tego języka…
Dodam od razu, że na pierwszym semestrze miałem programowanie funkcyjne w OCamlu, więc coś tam wiem. F# był na OCamlu wzorowany i składnia jest trochę podobna. Jednak będę patrzył na to bardziej z perspektywy C#.
Na początek dostajemy coś co znamy
namespace SharpOffice.Common.Tests
Deklaracja naszej przestrzeni nazw. No to dalej chcemy usingi. W F# jest prawie tak samo. Słowo using
zastąpione jest przez open
.
open Moq
open NUnit.Framework
open SharpOffice.Common.Commands
open SharpOffice.Core.Commands
I w tym momencie mamy dwie ścieżki. Możemy utworzyć klasę (type
) albo moduł, czyli klasę statyczną (module
). Ja wybrałem moduł i opatrzywszy go odpowiednim atrybutem uzyskałem
[<TestFixture>]
module UndoStackTest =
//...
Warto tu zauważyć, że w F# wcięcia mają znaczenie (tak jak w Pythonie).
Kolejnym krokiem było utworzenie sobie zmiennej. W F# pierwszeństo mają stałe, które deklaruje się za pomocą let
. Zmienne deklaruje się przez dodanie słowa mutable
, czyli zmienny.
let mutable _undoStack = new UndoStack()
Następnie aby uprościć sobie nieco pisanie moich testów postanowiłem skrócić wykonanie metod na _undoStack
przez opakowanie je w funkcje.
let Undo () = _undoStack.Undo()
let Redo () = _undoStack.Redo()
Odkryłem przy tym dość ciekawą rzecz. Jeśli zrobimy let Undo = _undoStack.Undo
to działa to jak przypisanie delegatu. Miałem z tego powodu błędy po uruchomieniu testów i trochę się męczyłem zanim doszedłem o co chodzi.
Następnie potrzebowałem utworzenia nowego obiektu UndoStack
przed każdym testem.
[<SetUp>]
let Clear () = _undoStack <- new UndoStack()
Jak widać do przypisania nowej wartości do zmiennej używamy <-
. W ten sam sposób używa się własności.
Musiałem też ignorować sporo funkcji, które zwracały wartość, której nie używałem.
[<Test>]
let ThrowOnEmptyStackTest () =
ignore(Assert.Throws<EmptyStackException>(fun () -> ignore(Undo())))
Podsumowanie
Co na tej całej migracji zyskuję? Po pierwszie rochę mniej kodu. Po drugie uczę się nowego języka. I co najlepsze: jest on wspierany przez Mono, więc nie mam problemu z kompilacją pod Linuxem.
Jeśli chcecie zobaczyć moje testy zajrzyjcie do SharpOffice.Common.Tests.