Od jakiegoś czasu w kręgach Ruby on Rails można zauważyć przesunięcie paradygmatu w zakresie metodologii testowania kodu. Popularyzowane podejście TDD (Test Driven Development) zostaje wypierane przez BDD (Behaviour Driven Development). Jedną z bardziej promowanych bibliotek jest RSpec. Mimo że kusi składnią przypominającą naturalny język angielski, próba wykorzystania RSPec w rzeczywistym projekcie szybko może stać się co najmniej kłopotliwe.
RSpec aby wyglądał tak ładnie jak wygląda, wykorzystuje dynamikę i specyficzne właściwości Rubiego. Ruby pozwala opuszczać nawiasy w metodach, niezauważenie otwierać i modyfikować klasy swoich bibliotek (w tym wbudowanych i standardowych) oraz ogólnie dosyć dobrze nadaje się do tworzenia nowych języków do specyficznych zadań (DSL, Domain Specific Language). Innymi słowy, ceną za korzystanie ze składni RSpec jest konieczność opanowania tego nowego języka. I tu zaczynają się schody…
Dokumentacja do RSpec jest najdelikatniej mówiąc, niekompletna. Przykładów użycia RSpec trzeba szukać po całym internecie. Dokumentacja wygenerowana z kodu źródłowego API do RSpec jest również słabej jakości. Brakuje przykładów, brakuje nawet wzmianki co do niektórych metod, jakie są używane. Np. weźmy taki prosty przykład:
response.should have_text(/My String/)
Próba znalezienia czegoś sensownego na stronie z API RSpec’a jest beznadziejna. Nie ma nic napisane o metodzie “response”, nie ma nic o metodzie “have_text” ani w ogóle o tym, co można przekazać do metody should(). Po prostu pojawia się coś niczym królik z kapelusza magika. I tak jest z wieloma elementami składni RSpec’a. Są jakieś pojedyńcze elementy, ale nie wiadomo które, i jak je łączyć ze sobą. Jeśli Ruby ma zbytnie skłonności do “magii”, to RSpec magia do kwadratu. Coś się pojawia i znika nie wiadomo skąd, ani dlaczego. To jest po prostu chore. Po wielu dniach takiej walki z RSpec doszedłem do wniosku, że to droga donikąd. Tylko, że z drugiej strony, cofnięcie się z BDD do TDD też nie brzmi atrakcyjnie.
Czy jest zatem jakieś wyjście pośrednie? Tzn. rozwiązanie które nie rezygnując z elegancji BDD byłoby oparty na znanych, dobrze opisanych metodach, bez żadnej kolejnej, magicznej składni? Wydaje się, że powyższe założenia spełnia projekt Shoulda. Mój pierwsze wrażenia z kontaktu z tą biblioteką, jak na razie, są dosyć dobre. Shoulda wprowadza dużo mniej nowej składni niż RSpec. Większość testów wykorzystuje stare, sprawdzone “asserty”. I o to chodzi. Nowa metodologia i znana, sprawdzona biblioteka standardowa Rubiego Test::Unit.
class UserTest << Test::Unit
context "A User instance" do
setup do
@user = User.find(:first)
end
should "return its full name"
assert_equal 'John Doe', @user.full_name
end
context "with a profile" do
setup do
@user.profile = Profile.find(:first)
end
should "return true when sent #has_profile?"
assert @user.has_profile?
end
end
end
end
Jeszcze jedna uwaga odnośnie implementacji BDD w innych językach. Trochę miałem problemów ze znalezieniem biblioteki do Pythona. Znaleziona obszerna lista bibliotek do Pythona nie ma kategorii BDD. Wygląda na to, że BDD i w ogóle kwestią testowania bardziej przejmują się użytkownicy Rubiego. Ale na szczęście na stronie na stronie http://behaviour-driven.org/Implementations zamieszczono informacje których mi brakowało. Jeden z linków prowadzi do Pinocchio, który jest rozszerzeniem bibliotek Nose używanej m.in. przez framework Pylons. Co ciekawe, jest też nawet JSSpec, biblioteka BDD dla języka Javascript.
Zobacz też:
Beyond Test Driven Development: Behaviour Driven Development
Wykładowcą jest Dave Astels, współautor książki A Practical Guide to eXtreme Programming (Google TechTalks)