Shoulda - pozbycie się magii RSpec'a

Opublikowane przez Jarosław Zabiełło Sun, 28 Oct 2007 17:45:00 GMT

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)

Tagi , , , , , , , ,  | 11 comments

Comments

  1. Avatar ocher powiedział about 22 hours later:

    Mnie sie bardziej podoba test/spec. Do poczytania tutaj: http://errtheblog.com/post/4268 Polecam.

  2. Avatar Jarosław Zabiełło powiedział about 23 hours later:

    Wygląda na to, że Spec/Test realizuje podobny pomysł co Shoulda. W każdym razie co by nie wybrać, lepsze to niż magiczny RSpec.

  3. Avatar szeryf powiedział 1 day later:

    Moje odczucia do RSpeca ewoluowaly podobnie do twoich: na poczatku spodobala mi sie niby-naturalna skladnia i sposob organizacji testow. Potem entuzjazm troche przygasl, a gdy ostatnio mialem do czynienia z dosc sporym systemem, w ktorym zamiast test/unit uzywano RSpeca—entuzjazm zamienil sie w niechec.

    Teraz stosuje shoulda i jestem zadowolony. Najwieksza zaleta wydaje mi sie mozliwosc bezproblemowej integracji z tradycyjnym testami. Dzieki temu nie ma problemow, jezeli nie wszyscy programisci w projekcie lubia nowiniki :)

    A co do podzialu TDD=test/unit, BDD=RSpec, to nie bylbym taki pewny. IMHO BDD mozna praktykowac rowniez przy uzyciu test/unit.

  4. Avatar Kaminski Pawel powiedział 1 day later:

    A ja zdecydowanie pozostane przy Rspec, chociaz musze przyznac ze dokumentacja rzeczywiscie pozostawia wiele do zyczenia. Bardzo pomocna natomiast jest lista dyskusyjna rspeca gdzie wiekszosc pytan zostaje rozwiazanych poprzez samych tworcow min. D.Chelimsky czy A.Hellesoy. I “plain text stories” wygladaja rzeczywiscie imponujaco!

  5. Avatar Radarek powiedział 1 day later:

    No właśnie miałem dokładnie takie same odczucia odnośnie RSpec. Przeglądałem kiedyś jego dokumentację, ale nijak to się miało do tego co widziałem na blogach, tutorialach. Nie znalazłem nigdzie dokładnego opis jakie konstrukcje można stosować. Raz jest smth.should be_xxx, raz smth.should.be.xxx. To co jest fajne to czytelność, bo niezależnie od tego czy to była 1 czy 2 forma umiem to przeczytać. Ale przecież wcześniej to należy napisać…

    Wydaje mi się, że warto jednak dać mu szansę, ale ograniczyć się do kilku typowych konstrukcji.

  6. Avatar maniel powiedział 2 days later:

    hmm, czy sampel na stronie domowej projektu [ten który skopiowałeś] nie jest czasami wadliwy? chodzi mi o `should`, tam nie powinno być ‘do’ po parametrze?:)

  7. Avatar Jarosław Zabiełło powiedział 3 days later:

    maniel: Nie. Przykład jest skopiowany z screencasta RSpec, part III

    Kaminski Pawel: “Plain text stories” można równie dobrze wykonać na bazie biblioteki Shoulda.

  8. Avatar Limak powiedział 10 days later:

    To ja mam pytanie – czy ktoś wie jak w Shoulda wyrzucić powtarzającą się w dwóch kontekstach funkcjonalność do metody – bo narzeka, że zdefiniowanej metody nie widzi…

  9. Avatar Radarek powiedział 11 days later:

    Wróciłem jeszcze do tego tematu, bo zagłębiam się trochę w bdd i rspec (a jednak:)).

    Ten przykład jednak ma błąd, tak jak zwrócił uwagę maniel. Brakuje “do” w:

    should “return true when sent #has_profile?” assert @user.has_profile? end

    Na stronie domoweh shoulda jest z ‘do’.

  10. Avatar szeryf powiedział 15 days later:

    Limak: może metoda powinna być metodą klasy? (def self.method…)

  11. Avatar Tomasz Mazur powiedział about 1 year later:

    Jak sie okazuje można shoulda i rspec pozenic ze soba :)

    http://github.com/carlosbrando/remarkable/tree/master

    pierwsze wrazenia bardzo pozytywne (choc natknalem sie na pare bugów)

(leave url/email »)

   Pomoc języka formatowania Obejrzyj komentarz