niedziela, 13 listopada 2011

Gmock

W TDD istnieją dwa podejścia do testów interakcji:
  • klasyczne, gdzie wykorzystuje się prawdziwe obiekty, jeśli można
  • z wykorzystaniem obiektów pozornych (Mock objects) 
Drugie podejście jest bliższe idei testów jednostkowych, ponieważ pozwala przetestować kod w prawdziwej izolacji od innych obiektów. Dzięki dobremu wykorzystaniu obiektów pozornych można łatwo sprawdzić interakcje między obiektami, nawet jeśli nie zwracają one żadnych wartości.

Takie podejście w projektach opartych o architekturę MVC, do których oczywiście zaliczają się Grailsy, jest bardzo przydatne m.in. w kontrolerach, które zwykle operują na obiektach klas domenowych czy też serwisów. Dzięki zastosowaniu mocków możemy łatwo sprawdzić czy kontroler wykonuje powierzone mu zadania i nie jesteśmy uzależnieni od faktycznej implementacji innych warstw.

Dzięki dynamiczności języka Groovy, obiekty pozorne można tworzyć bez użycia wyspecjalizowanych frameworków, jednakże jest to dość niewygodne. Powstało kilka projektów mających na celu uczynienie tego łatwiejszym. Jeden z nich (MockFor) jest zintegrowany w Groovy on Grails, jednakże mi osobiście nie podpasował. Wobec czego polecam Gmock, z którego sam także korzystam.

Instalacja Gmock w projekcie Grails nie jest skomplikowana, jednakże aktualnie nie istnieje żaden plugin, który bo to jeszcze ułatwiał. Skorzystamy, więc z Mavena i pliku conf/BuildConfig.groovy. W sekcji odpowiadającej za dependencies dopisujemy:
test 'org.gmock:gmock:0.8.1'
Aby wykorzystać Gmock w swoim teście możemy zastosować jedną z 3 metod:
  • dziedziczyć po klasie GMockTestCase (to jest opisane w dokumentacji Gmock, jednakże to podejście nie jest najlepsze z różnych przyczyn, więc nie będę go szerzej opisywał)
  • dodać adnotację @WithGMock do klasy testu (to jest najfajniejsze rozwiązanie, jednakże w aktualnej wersji nie działa z Grails 2.0)
  • użyć obiektu typu GMockController() 
We wszystkich poniższych przykładach zakładam, że istnieje już obiekt typu GMockController o nazwie gmock (jeśli ktoś korzysta z metody 1 lub 2, to we wszelkich wołaniach można go pominąć. Czyli zamiast gmock.metoda() piszemy metoda()).

 Tworzenie obiektów pozornych jest bardzo łatwe i opiera się na prostym DSL stworzonym w tym celu.

I tak dla klasy SpringSecurityService tworzymy go w następujący sposób:
def springSecurityMock = gmock.mock(SpringSecurityService)
W ten sposób mamy utworzony obiekt pozorny, jednakże jeszcze bez żadnych metod, czy też sprawdzania warunków. Aby dodać warunki skorzystamy z poniższego kodu:

springSecurityMock.encodePassword("password").returns("asd").times(1)
 Powyższy kod można rozbić na kilka członów:
  • springSecurityMock.encodePassword("password") - jest to zdefiniowanie metody encodePassword, która spodziewa się podanego parametru
  • returns("asd") - zdefiniowane wartości zwracanej dla metody z poprzedniego kroku
  • .times(1) - nasze założenie testowe
Powyższy kod można wobec tego przeczytać jako: spodziewaj się jednokrotnego wywołania metody encodePassword z parametrem "password" i zwróc "asd".

Tak przygotowane założenie sprawdzamy za pomocą metody play:
gmock.play { sut.someMethodThatCallsEncodePassword() }

 Poniżej prezentuję całą metodę testową z wykorzystaniem Gmock:
@Test
void shouldCallEncodePassowrd() {
  def gmock = new GMockController()
  sut.springSecurity = gmock.mock(SpringSecurityService)
  sut.springSecurity.
    encodePassword("password").returns("asd").times(1)   gmock.play { sut.someMethodThatCallsEncodePassword() } }
Powyższy kod wykona się poprawnie, gdy metoda  someMethodThatCallsEncodePassword faktycznie zawoła metodę encodePassword z parametrem "password".

Gmock jest bardzo ciekawą biblioteką, ponieważ potrafi podmieniać także metody statyczne, bądź też metody na żywych obiektach (tzw. partial mock), jednakże o tym w następnych postach.

Brak komentarzy:

Prześlij komentarz