Monthly Archives: October 2009

RSpec + machinist pradžiamokslis (I dalis)

Ruby on Rails tikrai daro įspūdį pradedantiesiems kaip greit ir natūraliai galima sukurti web aplikaciją. Tačiau panorėjus pasekti Rails guru pėdomis, kurie kalbant apie kokybę visi vienu balsu tvirtina “always, always test!”, tikrai galima atsimušti į informacijos pradedantiesiems trūkumą, ypač jeigu norit pasikinkyti ne standartinį Test::Unit. Ne pirmą kartą girdint tokį nusiskundimą, nusprendžiau sudėlioti trumpą RSpec ir machinist pradžiamokslį – kaip instaliuoti, kaip susikonfigūruoti, nuo ko ir kaip pradėti, kur/kaip ieškoti informacijos.Kodėl būtent RSpec ir machinist?RSpec leidžia aprašyti sistemos elgseną labiau “ruby way”, negu Test::Unit, nors pastarasis vis dar dažnai naudojamas. RSpec laikomas behaviour-driven development įrankiu, nors šiuo atveju parodysiu tiesiog test-driven development panaudojimą.Sistemai augant išlaikyti reikiamą fixtures kiekį ir struktūra yra tikras vargas, todėl apžiūrėkime machinist, kuris suteikia galimybę sugeneruoti duomenis, kurių reikšmės mums nesvarbios. Kodėl ne factory_girl? Tiesiog, kodas atrodo gražiau ;-)SąlygosŠis pradžiamokslis nėra skirtas įrodyti, kad jums reikia testuoti. Jis taip pat nebus naudingas pažengusiems, kuriems ši informacija gali atrodyti savaime suprantama. Visgi turite būti susipažinę su pagrindiniais Ruby on Rails principais. Pavyzdžiai skirti Rails 2.3.4, RSpec 1.2.9, machinist 1.0.3 ir faker 0.3.1, tačiau greičiausiai veiks ir su kitomis šių paketų versijomis.PradžiaTarkime, kad norime padaryti aukcioną, kuriame galima kelti kainą iki tam tikros datos.

rails auction_example

config/environments/test.rb pridedame:

config.gem "rspec", :lib => false, :version => ">= 1.2.9"config.gem "rspec-rails", :lib => false, :version => ">= 1.2.9"

Instaliuojame, jeigu dar neturime:

rake gems:install RAILS_ENV=test

Ir sugeneruojame RSpec failiukus:

ruby script/generate rspec

Jau pasiruošę pradėti!Pirma pavaraAkivaizdu, kad turėsime prekes, kurias norėsime brangiai prakalti piniguotiems dėdėms iš užsienio:

ruby script/generate rspec_scaffold product name:string auction_ends_at:datetime

rspec_scaffold generatorius parūpino mums ne tik įprastinius scaffold failus, bet ir pradinius griaučius testavimui. Analogiškos komandos yra rspec_controller, rspec_model. Faile spec/models/product_spec.rb rasite jau tokį tekstuką:

require 'spec_helper'describe Product do  before(:each) do    @valid_attributes = {      :name => "value for name",      :auction_ends_at => Time.now    }  end  it "should create a new instance given valid attributes" do    Product.create!(@valid_attributes)  endend

Manau, kad nieko papildomai aiškinti nereikia, geriau pulkime ir pažiūrėkime ar tikrai veikia:

rake db:migraterake spec#=>............................. Finished in 1.296021 seconds29 examples, 0 failures

Kol kas viskas kaip per sviestą. Kadangi darome aukcioną, akivaizdu, jog jeigu prekei skirtas laikas baigėsi, daugiau statymų daryti nebegalima. Žinoma, tam prireiks Product modelyje metodo auction_ended?. Pagal TDD pirma turime parašyti testą:

# iškart po it "..." do - end blokoit "should mark auction as ended if it's so" do   product = Product.create(@valid_attributes.merge(:auction_ends_at => 2.hours.ago))   product.auction_ended?.should be_trueend

Ir žinoma, išbandę rake spec gausime pranešimą:

1)NoMethodError in 'Product should mark auction ended if it's so'undefined method `auction_ended?' for #Product:0xb731808c./spec/models/product_spec.rb:17:Finished in 1.070546 seconds30 examples, 1 failure

Dabar jau galima bandyti rašyti kodą, kuris tenkintų esamus testus:

class Product Ir jau su tokiu kodu gauname išganingajį pranešimą:
30 examples, 0 failures

Bet juk kodas tai neteisingas, tiesa? Čia yra svarbiausia žinutė – testuokite įvairius variantus. Jeigu metode yra if sąlyga, apeikite visas šakas:

it "shouldn't mark auction as ended if it ends in the future" do  product = Product.create(@valid_attributes.merge(:auction_ends_at => 2.hours.from_now))  product.auction_ended?.should be_falseend

Dabar jau, žinoma, turime 1 failure. Teliko pamodifikuoti Product metodą, kad jis atitiktų realybę:

def auction_ended?  auction_ends_at Dabar mūsų parašyti testai ne tik tuščiai prasisuka bet ir praneš apie galimą problemą jeigu ką nors tvarkydami subjaurosim šio metodo prasmę. Realiai toks testavimo tikslas ir yra - būti užtikrintam, kad keičiant ką nors viename gale, kitame viskas veikia tiksliai taip, kaip turėtų. Antroje dalyje - kam reikalingas machinist, kodėl jis geresnis negu standartiniai fixtures ir pavyzdys kaip testuoti controllerius.Kad lengviau galėtumėt pabandyti kaip viskas atrodo, sukūriau šios mini-serijos aplikacijos repozitoriją GitHub.

A kategorija

Media_httpemptydotcom_jhitc

Taip, pavyko! Regitros Yamaha FZ6 iš tikrųjų labai malonus ir “minkštas”. Garsiosios aštuonukės nėra kaip neįveikti ir jeigu vairavimo mokykloj ne varnas šaudėt tai vienintelis rimtesnis veiksnys čia yra susitvarkymas su nervais. Man su tuo iš esmės padėjo tai, kad prieš važinėdamas apvaikščiojau viską pėsčiomis, o aplink Regitrą A kategorijos maršrutais pasitryniau su automobiliu.Taigi sėkmingai A kategoriją nuo nulio pavyko išsilaikyti per 3 savaites. Prieš tai nebuvau net sėdėjęs ant tikro motociklo :-)Trumpai apie kaštus, jeigu kam įdomu:

  1. Teorijos egzaminas – 35 Lt
  2. Nuotraukos mokyklos pažymėjimui – 10 Lt
  3. Praktikos pamokos eksternu – 650 Lt (Motoakademijoje. Kaip matote, išmoko gerai :))
  4. Praktikos egzaminas – 111,50 Lt
  5. Naujo vairuotojo pažymėjimo išdavimas – 49 Lt

Taigi viso sėkmingai iš pirmo karto išlaikius visus egzaminus ir turint galiojančias medicinines ir pirmosios pagalbos kursų pažymas, kurios man tiko iš B kategorijos – 855,50 Lt. Medicininė ir pirmosios pagalbos kursai kainuoja kažkur apie 100 Lt.Gero kelio!