Gdy programuję w Ruby to do testowania wykorzystuję Rspec. Jednak testy, które piszę nie są zbyt wysokich lotów. Nigdy nie skupiałem się zbytnio nad dodatkowymi funkcjonalnościami tego narzędzia i co prawda efekt osiągałem, lecz było to odkupione dłuższym pisaniem. Natura moja jest leniwa więc zaczęła się bronić. Musiałem trochę więcej czasu spędzić nad lekturą dokumentacji aby teraz to zapunktowało krótszym i bardziej czytelniejszym kodem.
Pierwszą funkcjonalnością jest before. Możemy ją wykorzystywać na każdym
poziomie zagnieżdżenia.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
describe User do it "should be in any roles assigned to it" do user = User.new user.assign_role("assigned_role") user.should be_in_role("assigned_role") end it "should NOT be in any roles assigned to it" do user = User.new user.should_not be_in_role("unassigned_role") end end |
Powyższy przykład za: tym tutorialem.
Po uruchomieniu testu rspec this_spec.rb -cf nested
otrzymamy mniej
więcej taki wynik:
1 2 3 4 |
User should be in any roles assigned to it should NOT be in any roles assigned to it |
Jak widać w każdym teście model user jest tworzony na nowo. W tym
przykładzie jest to tylko wywoałanie User.new jednak tworzony model
jest trochę bardziej skomplikowany. W takim przypadku tworzenie modelu
możemy przenieść do oddzielnego bloku, na przykład tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
describe User do before do @user = User.new end it "should be in any roles assigned to it" do @user.assign_role("assigned_role") @user.should be_in_role("assigned_role") end it "should NOT be in any roles assigned to it" do @user.should_not be_in_role("unassigned_role") end end |
Wynik wykonania będzie taki sam a już mamy trochę mniej do pisania. No
tak, ale musimy w każdym teście używać tego modelu. Czy aby na pewno?
Na ratunek przychodzi nam subject.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
describe User do before do @user = User.new end subject { @user } it "should be in any roles assigned to it" do @user.assign_role("assigned_role") should be_in_role("assigned_role") end it "should NOT be in any roles assigned to it" do should_not be_in_role("unassigned_role") end end |
Subject pozwala na dopasowanie matcherów do określonego podmiotu.
Najpiękniejsze, że nie musi to być sam model ale także jego dane pole.
Jeżeli mamy w danym kontekście kilka testów sprawdzających name
danego modelu możemy
użyć:
1 2 |
subject { @user.name } |
i wszystkie matchery w tym bloku będą się odnosić do tego właśnie pola.
W ostatnim paragrafie wspomniałem o kontekście? No właśnie, context
jest blokiem, który grupuje kilka testów, może w nim użyć oddzielny blok
before
lub subject
, które bedą się odnosić tylko do tego bloku.
Możemy także trochę uprościć blok it. Możemy go skrócić do bloku
inline porzucając załączony opis. RSpec sprawnie matcher zamieni na opis
testu więc output powinien być dość czytelny. Aby jednak zachować w
miarę poprawną składnię językową otaczające konteksty powinny zaczynać
się od with lub when.
Efekt końcowy testu jest taki:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
describe User do before do @user = User.new end subject { @user } context "with assigned role" do before { @user.assign_role("assigned_role") } it { should be_in_role("assigned_role") } end context "without assigned role" do it {should_not be_in_role("unassigned_role") } end end |
Output wychodzi mniej więcej taki:
1 2 3 4 5 6 |
User with assigned role should be in role "assigned_role" without assigned role should not be in role "unassigned_role" |
Czysto i czytelnie. Mimo że w powyższym przykładzie nie widać do końca
różnicy to przy dłuższych testach jest ona wyraźna.
Ostatnie co zostało do omówienia to specify
. Blok ten może być
używany zamiast it
. Zamienia się je zwłaszcza wtedy, gdy nie potrzebny
jest żaden opis.
1 2 3 4 5 6 |
it "should be valid" do @user.should be_valid end specify { @user.should be_valid } |