Testy z F#

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.