Merb: cucumber + webrat, czyli wszystko co chcielibyście wiedzieć o testowaniu
02 grudnia 2008Tym razem będzie o testowaniu aplikacji napisanej w Merbie. Większość pewnie zna framework testujący RSpec. Razem z RSpecem dostępny jest Story Runner. Jednak jak można wyczytać na stronie projektu:
RSpec’s Story Runner is now deprecated and will be extracted out to a separate gem soon. For more info on cucumber, see http://github.com/aslakhellesoy/cucumber/wikisI to właśnie wspomnianego ogórka opisze.
Cucumber podobnie jak RSpec Story Runner służy do testowania (i dokumentacji) aplikacji za pomocą czytelnych dla każdego scenariuszy zapisanych w formie zwykłego tekstu. Taki scenariusz jest przetwarzany i dopasowywany do odpowiednich, zdefiniowanych w osobnym pliku kroków. Główne zalety frameworka to m.in. możliwość przetestowania całej aplikacji od modelu przez kontroler po widok tak, jakby to robił zwykły użytkownik oraz "darmowa" dokumentacja aplikacji w formie przykładów użycia.
(Jeśli ktoś nie jest zaznajomiony z tematem, to polecam "Plain text stories", a poza tym myślę, że przykład wszystko wyjaśni.)
Webrat natomiast pozwala na intuicyjne zapisanie tego, co robiłby użytkownik korzystający z aplikacji
visit home_path click_link "Sign up" fill_in "Email", :with => "good@example.com" select "Free account" click_button "Register"
Instalacja
Zakładam, że Merb jak i RSpec jest już zainstalowany ;)
webrat + cucumber
$ sudo gem install webrat cucumber
plugin merb_cucumber
Jeśli ktoś jeszcze nie dodał githuba do źródeł, to:
$ gem sources -a http://gems.github.coma potem wystarczy:
$ sudo gem install david-merb_cucumber
W katalogu aplikacji:
$ merb-gen cucumber --session-type webrat
Cucumber jest już zainstalowany i gotowy do pracy. Do katalogu aplikacji został dodany folder features, w którym to należy umieścić scenariusze. Warto przejrzeć zawartość tego folderu gdyż zawiera on ładny przykład scenariusza oraz kilka często używanych kroków.
W szczególności spodobał mi się plik features/steps/common_webrat.rb zawierający kroki opisujące działania wykonywane za pomocą webrata.
Może jakiś przykład?
Oto i on. Posłużę się kawałkiem obecnie rozwijanej przeze mnie aplikacji. Podaje kod modelu tylko po to, aby łatwiej było zrozumieć co się dzieje ;)
class Site include DataMapper::Resource property :id, Serial property :name, String, :nullable => false property :domain, String, :nullable => false property :description, Text property :keywords, String end
Kontroler oraz widoki praktycznie nie różnią się od tych wygenerowanych przez merb-gen resource - standard.
Scenariusze grupowane są w pliki "właściwości/cech" (feature). Warto teraz wspomnieć, że cucumber nie narzuca żadnych konwencji nazewnictwa. Ważne tylko aby pliki scenariuszy miały rozszerzenie .feature, a pliki kroków znajdowały się w folderze steps (kroki są oczywiście pisane w Ruby, więc pliki będą kończyły się na .rb) - nazwa nie ma znaczenia.
Na pierwszy ogień pójdzie dodawanie nowej strony. Na początku krótki opis:
Feature: Create site To create site An admin Must fill the form.Nie ma on większego znaczenia, jednak warto streścić tu zawarte niżej scenariusze.
Zajmijmy się teraz pierwszym przypadkiem - wypełniamy formularz, strona zostaje dodana i jesteśmy przekierowywani do listy wszystkich stron. A tak to wygląda:
Feature: Create site
To create site
An admin
Must fill the form.
Scenario: Creating new site
Given no sites exist
When I go to /sites
And I follow "Add site"
And I fill in "name" with "Cucumber"
And I fill in "domain" with "cucumber.org"
And I fill in "description" with "I love cucumbers"
And I press "Add"
Then I should see an notice message
And I should see table like
| Name | Domain |
| Cucumber | cucumber.org |
Zapisujac scenariusz (razem ze wstępem) jako create_site.feature i uruchamiając
$ rake featuresotrzymamy:
Jak widać, otrzymaliśmy ładnie pokolorowany scenariusz. Niebieskie kroki zostały pominięte ze względu na brak kroku dla pierwszej linii scenariusza. Żółty kolor oznacza właśnie, iż linia nie została dopasowana do żadnego kroku. Poniżej znajdują się kawałki kodu które można użyć do implementacji brakujących kroków.
Utwórzmy teraz plik steps/site_steps.rb a w nim:
Given /^no sites exist$/ do Site.all.destroy! end # ten krok przyda się na później, ale opisze go teraz Given /^site with name "(.*)" and domain "(.*)" exists$/ do |name, domain| Given "no sites exist" # wywołanie kroku znajdującego się powyżej Site.create(:name => name, :domain => domain) end Then /^I should see table like$/ do |table| table.hashes.first.keys.each do |head| response.should have_xpath("//table/tr/th[. = '#{head}']") end table.hashes.each do |hash| hash.each_pair do |key, value| response.should have_xpath("//table/tr/td[. = '#{value}']") end end end
Składnia kroków jest bardzo prosta: Given/When/Then + RegExp + blok opcjonalnie z parametrami.
Poprzez zastosowanie wyrażeń regularnych można wykorzystać ten sam krok dla różnych danych co znacznie zmniejsza ilość potrzebnych kroków. Przyczynie się do tego również możliwość wywoływania innych kroków z wewnątrz kroku. Ostatni krok wykorzystuje tabele i selektory XPath do sprawdzenia poprawności strony wynikowej.
Odpalmy testy ponownie. Powinno się pokazać wynik podobny do:
Działa :D
Dodajmy jeszcze jeden scenariusz, tym razem sprawdzający czy aplikacja zachowa się poprawnie w przypadku próby dodania strony o istniejącej już nazwie: (na koniec pliku create_site.feature)
Scenario: Creating site with existing name or domain
Given site with name "Cucumber" and domain "cucumber.org" exists
When I go to /sites
And I follow "Add site"
And I fill in "name" with "Cucumber"
And I fill in "domain" with "cucumber.org"
And I press "Add"
Then the creating request should fail
And I should see an error message
Wykorzystamy tu drugi krok, który utworzy w bazie stronę o nazwie "cucumber" i domenie "cucumber.org".
Po uruchomieniu okaże się, że nasza aplikacja nie przechodzi testu:
Aby to naprawić należy nice zmienić model:
property :name, String, :nullable => false, :unique => true property :domain, String, :nullable => false, :unique => trueI oto co uzyskamy:
Tadam ;]
Mam nadzieję, że ten krótki tutorial przybliży nieco zagadnienie testowania aplikacji za pomocą scenariuszy. Jeszcze raz polecam wiki cucumbera - można znaleźć tam odpowiedzi na wiele pytań, jak również zbiór przykładów i jeszcze kilku innych. W ostateczności można się o coś zapytać w komentarzach ;)
Na koniec mała ściąga all-in-one
sudo gem install webrat cucumber gem sources -a http://gems.github.com sudo gem install david-merb_cucumber merb-gen cucumber --session-type webrat rake featuresMiłego testowania.
Edit:
Dla tych co nie lubią/nie znają angielskiego albo po prostu chcą potem pokazać scenariusze komuś kto nie zna tego języka jest specjalna opcja. Cucumber pozwala tworzyć scenariusze w dowolnym języku, np. polskim*.
P.S. Na potrzeby tego wpisu blog musiał się poszerzyć o 120px :P
* - Może jutro to opisze, dzisiaj już nie mam siły myśleć.
EDIT: Autotest
Aby features uruchamiały się automatycznie wystarczy zainstalować najnowszą wersje merb_cucumber (sudo gem install david-merb_cucumber), uruchomić merb-gen cucumber w katalogu aplikacji oraz dodać do pliku cucumber.yml linijke z profilem autotest i opcjami np. takimi:
autotest: -r features --format pretty features
Opcja -r features automatycznie ładuje wszystkie pliki z katalogu features. Teraz wystarczy już tylko uruchomić autospec w katalogu aplikacji. (Działa pod rspec 1.1.11)
4 komentarze
ad. post scriptum: to zmniejszyc nie mogles obrazka / samej konsoli podczas uruchamiania? :)
byłoby nieczytelnie. (Dlaczego zawsze komentowana jest najmniej istotna cześć wpisu?)
Fajnie, że przybliżasz nam różne "merbowe" ciekawostki. Oby więcej takich tekstów.
ciekawe, ciekawe ;) aż nabrałem ochoty żeby się bliżej temu przyjrzeć xD
pozdrawiam :P