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