@Unroll w Spocku, przykład zaawansowany

Spock jest frameworkiem do testów, którego popularność w świecie JVM-a jest nie do podważenia.

W internecie można znaleźć wiele tutoriali, które wprowadzają w podstawy adnotacji @Unroll. Ogólnie chodzi w niej o to, że mamy możliwość uruchomienia tego samego kodu z wieloma zestawami danych. Najprostszy przykład użycia @Unroll wygląda tak:

Interesowało mnie, ile jeszcze można wycisnąć z tej adnotacji, i o tym będzie ten post.

Wymagania

Przy pisaniu procesorów, które tworzą encje takie, jakie Localization, potrzebowałem przetestować sytuację, gdy do procesora wchodzi encja reprezentująca stronę z Memory Alpha, posiadająca jakiś zestaw kategorii. Na podstawie obecności tych kategorii były ustawiane konkretne booleanowe flagi w encji wyjściowej, w tym przypadku właśnie Localization. Gdybym chciał to zapisać w kilkunastu czy kilkudziesięciu testach, powtórzyłbym bardzo dużo kodu.

Jednocześnie chciałem sprawdzić w każdej iteracji więcej, niż jedną daną wyjściową, ponieważ obok flagi, która w sytuacji obecności danej kategorii miała być ustawiona na prawdę, chciałem też sprawdzać liczbę nienullowych pól w całej encji wyjściowej.

Trzecim wymaganiem było, by nazwa flagi była przechowywana jako string. To także okazało się możliwe z @Unrollem. Chociaż zapewne to głównie zasługa Grooviego, który pozwala na dostęp do obiektów z użyciem tej samej składni, która umożliwia dostęp do map.

Ostatnim wymaganiem było, by zapisać w metodzie testowej interakcje, najlepiej z wyspecyfikowaną ich ilością. To też się prawie udało.

Implementacja

Ostateczna implementacja wygląda tam:

Opis implemenacji

W bloku given zostały zapisane wszystkie wymagane w każdej iteracji interakcje. Można by też po prostu dostarczyć do metody testowej już zaprogramowane stuby, które miałyby te same interakcje.

W bloku expect pobieramy lokalizację z procesora, a następnie, z użyciem nazwy flagi, którą też mamy w datasecie, porównujemy wartość flagi z kolejną wartością zapisaną w datasecie. Ostatecznie refleksją sprawdzamy ilość nienullowych pól w encji reprezentującej lokalizację.

W bloku where znajdują się dane. Tutaj największą niespodzianką jest to, że można w ramach bloków konstruować obiekty i odwoływać się o innych metod. Patrząc na przykłady w sieci, nie jest to wcale oczywiste.

Ograniczenia

Nie znalazłem sposobu, by wyspecyfikować ilość interakcji w ramach jednej metody testowej z @Unrollem. Interakcji nie można umieszczać w blokach given i expect, a z kolei blok then nie może współegzystować z blokiem expect. Jeśli więc chcemy sobie zaprogramować interakcje, możemy to zrobić tylko w bloku given, ale bez zapisywania przy nich ilości.

Podsumowanie

Umiejętnie użyty @Unroll pozwala napisać dowolny test, z wyjątkiem takiego, który zlicza ilość interakcji.

Kiedy używać @Unrolla, a kiedy jest on overkillem? Myślę, że dobrą granicą są cztery bardzo podobne metody testowe. Jeśli tyle mamy, należałoby je połączyć w jedną metodą z @Unrollem. Ale zgodnie z duchem reguły DRY, nawet 2 podobne metody testowe to nie byłoby za dużo, by zacząć używać @Unrolla.

A co, jeśli mimo wszystko chcemy zliczać interakcje i mieć data tables? Moje doświadczenie pokazuje, że lepiej rozdzielać kod, który głównie operuje na interakcjach z zależnościami, od kodu, który głównie operuje na danych. Jeśli powstaną dwie osobne klasy, albo przynajmniej dwie osobne publiczne metody, z których jedna operuje głównie na zależnościach klasy, a druga głównie przetwarza dane, problem z brakiem zliczania interakcji przy użyciu @Unrolla powinien zostać zminimalizowany.