Sinatra + Cucumber + Capybara

W piwnicy naszego biura znajduje się stół do Ping Ponga. Jakiś czas temu pojawił się pomysł, żeby zorganizować wewnętrzną ligę. Oczywiście nie ma mowy, żeby zapisywać punkty na kartce, zróbmy aplikację! Ponieważ będzie mało skomplikowana, to najlepiej w Sinatrze. Otestujmy ją ładnie i opiszmy na blogu.

Co powinna zawierać aplikacja:

  • Banalnie prostą rejestrację i logowanie
  • Możliwość dodawania rozgrywek
  • Listę wyników
  • Ranking

Z czego będziemy korzystać:

  • Sinatra – bo co za dużo Railsów, to niezdrowo.
  • Cucumber i Rspec – bo chcemy, żeby to było BDD.
  • Capybara – bo bardzo dobrze zastępuje Webrata, a w sieci jest bardzo mało przykładów wykorzystania jej z Sinatrą

Dla kogo jest ten turorial:

  • Dla osób, które chcą poznać Sinatrę.
  • Dla osób, które nie znają BDD, cucumbera i jego ekosystemu.
  • Dla tych, którzy znają jedno i drugie, ale są ciekawi jak to łatwo połączyć.

Zaczynamy

Zacznijmy od zainstalowania potrzebnych gemów. Na początek potrzebne nam będą:

gem install sinatra cucumber capybara rspec

Oprócz Sinatry, potrzebujemy Cucumbera, czyli narzędzia do uruchamiania testów integracyjnych, napisanych w języku, który nazywa się Gherkin, praktycznie brzmiących jak normalne (nieco sformalizowane) zdania. Każda linijka takiego testu, jest nazywana krokiem. Dla kroków pisane są ich definicje, czyli wyrażenia regularne, w których zawarta jest treść testu. W trakcie uruchomienia testu, kroki są analizowane pod względem zgodności z definicjami i jeśli do którejś pasują, następuje wywołanie zawartego w definicji kroku testu. Capybara umożliwia testowanie aplikacji opartych o Rack i dostarcza metody do wysyłania zapytań do serwerów rackowych, oraz analizowaniu odpowiedzi. Czyli na przykład sprawdzaniu, czy na stronie znajduje się jakiś tekst, lub wypełnianiu i wysyłaniu formularzy. W końcu Rspec to podstawowa biblioteka do BDD w Rubym, dostarczająca szereg przydatnych metod, zastępujących Test::Unit.

Hello World w Sinatrze

W pierwszym kroku stworzymy zaczyn aplikacji, potem dopiero napiszemy do niej test. Utwórzmy plik pong.rb z następującą zawartością:

require 'rubygems'
require 'sinatra'

get '/' do
   'Hello ping-pong!'
end

Te pięć linijek, to najprostsze Hello World w Sinatrze. Sprawdźmy zatem, czy działa, uruchamiając serwer z konsoli:

ruby pong.rb

W tym momencie, na localhoście na porcie 4567 powinniśmy ujrzeć Hello ping-pong.
Teraz pora na ciekawszą część.

Konfiguracja środowiska testowego

mkdir features

W tym katalogu trzymane są wszystkie scenariusze uruchamiane poleceniem cucumber. W nim powinien się znaleźć katalog support. W nim trzymamy wszystkie pliki, które są potrzebne przy starcie cucumber’a, a nie są definicjami kroków. Jednym z nich jest features/support/env.rb i to w nim umieszcza się konfigurację środowiska. Po pierwsze musimy wczytać utworzony wcześniej plik startowy aplikacji.

require File.join(File.dirname(__FILE__), '..', '..', 'pong')

Będziemy również potrzebowali następujących bibliotek:

require 'capybara'
require 'capybara/cucumber'
require 'spec'

Cucumber uruchamia scenariusze w obiekcie, który nazywa się World. Wszystkie metody zdefiniowane wewnątrz tego obiektu są dostępne w definicjach kroków. Zależy nam na dwóch rzeczach: chcemy uruchamiać scenariusze z użyciem capybary, oraz móc korzystać z method rspeca. Musimy więc dodać poniższy wpis w pliku features/support/env.rb:

World do

  Capybara.app = Sinatra::Application

  include Capybara
  include Spec::Expectations
  include Spec::Matchers
end

Tworzymy pierwszy scenariusz

Mając tak skonfigurowane środowisko, stwórzmy pierwszy scenariusz: features/home_page.feature.

Feature: Home page
  In order to join ping-pong league
  As a guest
  I want to see home page of ping pong app

  Scenario: Accessing home page
    Given I am the guest
    When I go to the home page
    Then I should see "Hello ping-pong" 

Na początku pliku znajdują się nasze założenia i opis, wyjaśniający, dlaczego dana funkcjonalność jest nam potrzebna. Właściwy scenariusz (czyli tekst, który wykonywany jest jako test), zaczyna się dopiero po w linijce 6, słowem kluczowym Scenario.

Gdy wywołamy polecenie cucumber, to zobaczymy, że nasz scenariusz nie przeszedł i dostaniemy informację, że należy zaimplementować następujące kroki:

Given /^I am the guest$/ do
  pending # express the regexp above with the code you wish you had
end

When /^I go to the home page$/ do
  pending # express the regexp above with the code you wish you had
end

Then /^I should see "([^\"]*)"$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end

Stwórzmy zatem katalog features/step_definitions. Wszystkie pliki rb, które się tam znajdą, zostaną domyślnie użyte przez cucumbera, do sparsowania kroków. Nasz plik może się nazywać sinatra_steps.rb. Wypełnijmy go.

Given, when, then oraz and, to słowa od których musi zaczynać się każdy krok. Praktycznie nie ma znaczenia, którego użyjemy, ale zdecydowanie zalecane jest, by używać tego słowa, które najbardziej odpowiada sytuacji.

Pierwszy krok, jest pusty, nie musimy nic robić, żeby zostać gościem.

Given /^I am the guest$/ do
end

Aby wywołać jakąś stronę, skorzystamy z funkcji visit, której podajemy ścieżkę do odwiedzenia.

When /^I go to the home page$/ do
  visit '/'
end

Napiszmy prosty warunek sprawdzający, że na stronie znajduje się poszukiwany tekst.

Then /^(?:|I )should see "([^\"]*)"$/ do |text|
  page.should have_content(text)
end

Ponownie wywołajmy cucumber, by naszym oczom ukazał się zielony widok.

1 scenario (1 passed)
3 steps (3 passed)

Bingo! Znaczy to, że scenariusz przeszedł pomyślnie i wszystkie trzy kroki są spełnione. Właśnie napisaliśmy i przetestowaliśmy najprostszą aplikację w Sinatrze.

W następnym odcinku

Mając wszystko ładnie przygotowane, będziemy mogli się zająć dodaniem obsługi bazy danych i umożliwić użytkownikom rejestrację i logowanie, ale o tym w następnym odcinku.

Przykład dostępny jest oczywiście na githubie: http://github.com/tjeden/sinatra-pong/tree/0.1

Część 2:
http://rubysfera.pl/2010/5/uwierzytelnianie-w-sinatrze-z-data-mapperem



Komentarze

  1. Seban 15.04.2010

    Comment Arrow

    U mnie w piwnicy 🙂 są darty, fajnie by było jakby wasza aplikacja mogła obsługiwać nie tylko ping-ponga.


  2. tjeden 15.04.2010

    Comment Arrow

    Model będzie się nazywał pewnie game i składał z setów, więc po drobnych modyfikacjach nie powinno być problemu, żeby dostosować do dartsów (zależy to jeszcze od reguł gry). Wystarczy przecież fork na githubie. 🙂


  3. Graziano 15.04.2010

    Comment Arrow

    C’mon, we got no time for explanation. Może w końcu pogramy. Trzymam kciuki.


  4. Tubis 15.04.2010

    Comment Arrow

    Bardzo fajna inicjatywa 🙂 Podoba mi się dlatego, że dla mnie jak i pewnie dla większości początkujących “railsowc’ów” to testowanie jest najtrudniejsze.

    Keep it up!


  5. tjeden 15.04.2010

    Comment Arrow

    Następna część pojawi się zatem już w przyszłym tygodniu.


  6. Czak 15.04.2010

    Comment Arrow

    Obiecanki macanki…


  7. tjeden 15.04.2010

    Comment Arrow

    Tutoriale pisze się dłużej niż jeden dzień. Poza tym jeszcze nie wszystko poprawnie działa.


  8. gosc 15.04.2010

    Comment Arrow

    fajny serwis, fajny tutorial


  9. online 15.04.2010

    Comment Arrow

    Nauczylem sie wiele




O autorze

Aleksander Dąbrowski

Od 2008 zawodowo programuje w Ruby i Railsach. Jest maniakiem prostych i eleganckich rozwiązań, nie boi się usuwania brzydkiego kodu. Uwielbia dzielić się wiedzą, a w wolnych chwilach naprawia samochody.