Groovy to bardzo ciekawy język. Działa na JVM (maszynie wirtualnej Javy), jest kompatybilny z składnią tejże Java’y, jest jednocześnie statyczny, dynamiczny, skryptowy i równocześnie … nie skryptowy. Z naszego punktu widzenia jest także „modny”, dlatego, że używamy go powszechnie i namiętnie w Streamsoft Next i Verto do dostarczania dynamicznych zachowań systemu. Osobiście muszę przyznać, że uwielbiam te zazwyczaj parę linii kodu, które „robi robotę”, bez kompilatora, cyklu wydawniczego, aktualizacji wersji i ogromu czasu który to zazwyczaj zajmuje. Oczywiście jak każdy z bagażem Javy, zaczynałem przygodę z Groovy jako szybką „uruchamiarką” kodu. Dopiero później, nieśmiało i powoli zagłębiając się w jego cechy dostrzegłem moc tkwiącą w bardzo zwięzłej składni, wielu ciekawych konstrukcjach, uproszczeniach i ułatwieniach. Śledząc rozwój Groov’iego nie sposób nie zauważyć jak na bazie tych przyjaznych językowych „tworów”, powstają różne „ciekawe i modne” narzędzia, wymienić można te najbardziej znane jak Grails, Gradle, Glide czy Spock.

Postarajmy się dostrzec moc Groovy’iego analizując ostatni framework z listy. Spock to po prostu kolejny framework do testów dla szeroko-rozumianej platformy Java. Ale czy na pewno tylko „kolejny”? Przebijając się przez coraz to nowsze wersje Junit’a, ćwicząc TestNg, wspierając się asercjami Hamcrest’a czy AssertJ’a a nawet z rzadka mockując Mockito’em, zawsze czułem niemałą „ortpedię” tych rozwiązań. Cóż, jest jak jest, trzeba sobie radzić tym co mamy. Ale, gdy po raz pierwszy widzimy jak wygląda test w Spock’u, możemy się nieźle zdziwić:

test w Spock’u

Od początku. W końcu można „po ludzku” napisać co sprawdza test, bez żadnych „wielbłądów” i innych utrudnień składniowych. Mamy także regularne sekcje „given/when/then” a nie jakieś sztuczne komentarze. Co zaskakujące w sekcji „then” nie ma asercji, to dziwne, ale jednak jakoś to działa (o tym dalej). Zobaczmy co się stanie jak uruchomimy ten test (w teście jest błąd aby pokazać jak wygląda wynik niespełnionego testu). Test uruchomiłem klasycznie Maven’em ale także z IDE za pomocą polecenia „Run as – JUnit test” (to też ciekawe). Wynik jest taki:

wynik niespełnionego testu

Nie dość, że test uruchomił się jako Junit’owy, dodatkowo jakoś magicznie pojawiła się asercja, to jeszcze wyrażenie zostało rozłożone na elementy składowe i mamy wyraźnie podane wartości oraz wskazane gdzie się nie zgadza wynik. Może być lepiej? Sprawdźmy jeszcze jak wygląda test dla zbioru wartości:

test dla zbioru wartości

Pojawiło się „where” (nowe słowo kluczowe ?), które w dziwny sposób robi całą robotę związaną z wielokrotnym wywołaniem testu dla podanej listy parametrów, znowu jest elegancko i prosto.

ak zatem to zrobili? Nazwa metody jako string, to standardowa możliwość języka, nie ma tu żadnych czarów. Etykiety „given:”, „when:” i kolejne, są to zwykłe etykiety języka, takie same jak w Java – pamiętacie jeszcze, że są? Interesująca jest magiczna sekcja „where:”, bez jawnych asercji … tu jest ciekawie, nad tym warto się pochylić. Otóż Groovy posiada otwarty cykl kompilacji kodu, w którym na pewnym etapie powstaje reprezentacja kodu w postaci AST (Abstract Synax Tree). AST to drzewo o określonej strukturze, gdzie każda instrukcja, każdy element kodu reprezentowany jest przez pewną gałąź. Jak jest już zbudowane AST, to następnie odbywa się tworzenie kodu wynikowego. Twórcy języka zostawili furtkę każdemu kto chciałby wpiąć się w cykl kompilacji i np. zmodyfikować AST, dodając coś od siebie. Z tej możliwości skorzystali twórcy Spock’a, dodali własny mechanizm transformacji AST, wprowadzając modyfikacje pozwalające na powyższą „magię”. Nasz kod z pierwszego testu przed zmianami Spock’a w AST wygląda tak:

kod z pierwszego testu przed zmianami Spock’a w AST

a po działaniu klasy „org.spockframework.compiler.SpockTransform” wygląda już tak:

po działaniu klasy „org.spockframework.compiler.SpockTransform” wygląda tak

i wszystko jasne, dodawany jest kod od śledzenia wartości, sprawdzenia warunku (asercja), numery linii i kolumny, tekst warunku, czyli cała magia potrzebna do weryfikacji i czytelnego przedstawienia wyniku w razie niespełnienia warunku. Elegancko i efektywnie. Dodajmy, że na „given/when/then/where” świat Spocka się nie kończy, mamy jeszcze „expect/setup/cleanup/and/” oraz pokaźny zbiór operatorów, asercji, mock’ów i innych konstrukcji pozwalających na szybkie i wygodne tworzenie naprawdę czytelnych testów, z dużymi szansami na to, że ktoś po nas je zrozumie.

Często można przeczytać, że „Spock is so groovy”, zgadzam się z tym w 100%. Zastanówmy się cóż może oznaczać sukces Spock’a w szerszym ujęciu. Dla mnie jest to kolejny dowód na to, że nie ma jednego języka w którym warto pisać wszystko. Choć się da, to nie warto. Siła jaką pokazują języki domenowe (DSL) jest ogromna. Łatwość z jaką twórcy Spocka mogli – dzięki otwartości Groovy’iego – dostarczyć taki DSL, symboliczna i warta przemyślenia. Czy są tu gdzieś ukryte jakieś wady? Oczywiście. Spock, to kolejny „język” do nauki. Wprowadzając nową składnię … trzeba nauczyć siebie oraz nasze IDE aby ją rozumiały, podpowiadały, weryfikowały. Próbowałem tego z Eclipse … wyszło średnio, ale jednak coś tam zadziałało. Ktoś złośliwy powie, że rozbudowujemy naszą – niemałą już – wieżę Babel o kolejny język, prawda. Ale zobaczcie, działa to dalej na JVM, używa możliwości Groovy’iego, przyjęło cykl życia JUnita, czyli naprawdę mało „sztukaterii”, a wręcz sama elegancja i rozsądek. Zachęcam do bliższego spotkania z Groovy i Spock’iem, zapewniam, zaboli najwyżej lekko, a będzie ciekawie.

Krzysztof Olszewski

Krzysztof Olszewski

Dyrektor Technologii i Architektury Oprogramowania

Krzysztof Olszewski

Krzysztof Olszewski

Dyrektor Technologii i Architektury Oprogramowania