Posted by Saulius Grigaitis
Sun, 23 Nov 2008 21:35:00 GMT
Tiems, kas jau naudoja “Unit” lygio testus ir jaučia integracinio lygio testavimo poreikį verta išbandyti Cucumber, kuris pakeičią senąjį “RSpec Story” karkasą. Įrankis išties labai paprastas, gana gerai dokumentuotas, tad jį perprasti užtruks nedaug laiko. Be to, integracinius testus jau galima rašyti ir lietuvių kalba. Pvz.:
Savybė: Sudėtis
Norėdamas išvengti kvailų klaidų
Aš noriu, kad man pasakytų dviejų skaičių sumą
Scenarijus: dviejų skaičių sudėtis
Duota įvedžiau 50 į skaičiuotuvą
Ir įvedžiau 70 į skaičiuotuvą
Kai paspaudžiu "add"
Tada rezultatas ekrane turi būti 120
Ir rezultato klasė turi būti "Fixnum"
Daugiau pavyzdžių
| įvestis_1 | įvestis_2 | mygtukas | išvestis | klasė |
| 20 | 30 | add | 50 | Fixnum |
| 2 | 5 | add | 7 | Fixnum |
| 0 | 40 | add | 40 | Fixnum |
Skaidrės iš prezentacijos apie Cucumber irgi lietuviškos!
Tags BDD, cucumber, integration, rspec, testing | 2 comments | no trackbacks
Posted by Saulius Grigaitis
Fri, 06 Jul 2007 19:29:00 GMT
Sveiki. Pagaliau baigėsi bakalauriniai, stojamieji ir visi kiti akademiniai reikalai, tad dažniau rašysiu straipsnius. Šį kartą tęsiu pažintį su RSpec ir kontrolerių testavimu. Kontrolerių testavimo techninė dalis yra analogiška jau aptartoms dalims, bei turi keletą naujų dalykų, tačiau koncepciniai dalykai daug įdomesni. Juos ir aptarsiu šiame straipsnyje.
Visų pirma - RSpec'as propaguoja ne klasikinį testavimą, o "mock'inimą". Klasikiniame testavime visi testai surišti tarpusavyje panaudojant kiek galima tikrų objetų, o netikri objektai("mocks", "stubs" ir kita) yra naudojami tik tada, kai nėra kitos galimybės, pvz. dar neturint priėjimo prie WEB serviso, nes jis dar nesukurtas, bet turint jo API, bei darant prielaidą, kad servisas tikrai veiks korektiškai naudojantis pateiktu API. Tuo tarpu "Mock'istai" naudoja netikrus objektus kur tik gali ir tai turi prasmę. Iš esmės tai yra visiškai skirtingos koncepcijos, turinčios savo privalumų ir trūkumų. Pagrindiniai skirtumai yra testų integracija ir programavimo stilius. Klasikiniai testuotojai rašo testus, kurie surišti tarpusavyje, o "mock'istų" testai dažniausiai visiškai nepriklauso vienas nuo kito, tad klasikinių testuotojų testai yra integraciniai, o "mock'istų" - ne. Pvz. klasikinis testuotojas rašydamas kontrolerio testą nusprendžia, kad jam reikia naujo modelio "Book" klasės metodo "pages", jam nelieka nieko kito, kaip imti ir parašyti "Book" klasės metodo "pages" testą ir tada patį metodą "pages", geriausiu atveju - paprašyti kolegos, kad jis tai padarytų:). Gal viskas tuo gerai baigtųsi, jei nepaaiškėtu, kad "pages" metodui reikia dar kito metodo, kuris dar neparašytas, o kolega jau ir taip užimtas:). Taigi Klasikinis testuotojas norėjo parašyti kontrolerio testą ir patį kontrolerį, bet jam teko įsivelti į modelių metodų rašymą. Dažnai šita grandinė išauga iki įspūdingo ilgio. Tuo tarpu "mock'istas" daro prielaidą, kad modelis turi reikiamą metodą ar jį turės ir pasitiki jo veikimo korektiškumu, nes to metodo autorius jį jau ištestavo. Naudojant RSpec, tai atrodytų taip:
...
@book = mock_model("book")
@book.should_receive(:pages).and_return(100)
...
toks testas praeis sėkmingai net jei "Book" klasė ir neturės metodo "pages", tačiau programuotojas gali susikoncentruoti ir galvoti kaip geriau parašyti testą ir patį kontrolerį, o ne šokinėti prie kitų programos dalių.
Iš vienos pusės "mock'istams" lengviau programuoti, tačiau jie praranda testų integraciją, o klasikinimas testuotojams programuoti žymiai sunkiau, tačiau jie turi didesnę kodo kontrolę, nes integruoti testai geriau indikuoja pažeistas vietas po kodo pakeitimų. Klasikiniams testuotojams yra kur kas sunkiau programuoti didesnėse komandose lygiagrečiai, tuo tarpu "mock'istams" tai nedaro įtakos. Tačiau praktikoje kartais "mock'istams" sunku imituoti sudėtingų objektų elgseną, tad priveliama klaidų į pačius testus. Abi technikos turi savų pliusų ir minusų, jei dar neapsisprendei kuri tau tinkamesnė, paskaityk:
Martin Fowler - Mocks Aren't Stubs
RSpec Documentation - Mocks
Iki!
Tags BDD, Mocks, rails, rspec, ruby, Stubs | no comments | no trackbacks
Posted by Saulius Grigaitis
Mon, 07 May 2007 19:20:00 GMT
Sveiki. Pagaliau radau šiek tiek laisvo laiko. Šįkart kiek linksmiau:

RSpec!
BDD yra gana jauna metodika, evoliucionavusi iš gerai žinomo TDD(angl. Test Driven Development). TDD pagrindinė idėja yra kodą rašyti tik po to, kai parašomas tą kodą testuojantis testas. BDD perima šią metodiką, tačiau testo struktūra labiau panaši į specifikaciją ir ją iliustruojantį kodą, nei į sausus testus. Taigi naudojant BDD parašomas dokumentas, panašus į specifikaciją. Bene vienintelis patogus ir naudingas BDD įrankis Rails'ams yra
RSpec. Jis leidžia testuoti modelius, kontrolerius, view'us ir helper'ius. Taigi pradedam nuo modelių, paprastai, ten būna daugiausia logikos, kuri tiesiog prašosi ištestuojama.
./script/generate rspec_model notebook
mėgstamu editoriumi parašome pirmą spec'ą - užrašų knygutės turi turėti 100 puslapių
# spec/models/notebook.rb<br/>
require File.dirname(__FILE__) + '/../spec_helper'
describe "New notebook" do
fixtures :notebooks
before(:each) do
@notebook = Notebook(:my)
end
it "should have 100 pages" do
@notebook.pages.should eql(100)
end
end
ir fixture:
# spec/fixtures/notebook.yml
my:
id: 1
pages: 3
paleidžiame testą:
# ruby spec/models/notebook_spec.rb F
1)#ActiveRecord::StatementInvalid in "New notebook should have at least 2 pages"
Mysql::Error: Table 'project_test.notebooks' doesn't exist: SHOW FIELDS FROM notebooks
spec/models/notebook_spec.rb:5:in `new'
spec/models/notebook_spec.rb:5:
spec/models/notebook_spec.rb:3:
Finished in 0.482204 seconds
1 example, 1 failure
priminė, kad neturime notebooks lentelės. Perleidžiam migracijas ir klonuojame development duombazės struktūrą į test duombazę.
rake db:migrate
rake db:test:clone_structure
Vėl perleidžiame testą:
#ruby spec/models/notebook_spec.rb
F
1)#ActiveRecord::StatementInvalid in 'New notebook should have at least 2 pages'
Mysql::Error: Unknown column 'pages' in 'field list': INSERT INTO notebooks (`id`, `pages`) VALUES (1, 3)
spec/models/notebook_spec.rb:3:
Finished in 0.074569 seconds
1 example, 1 failure#
Hmm...neturime lauko "pages" , tad sugeneruojame migraciją
./script/generate migration AddColumnPagesToNotebook
ir ją užpildome:
class AddColumnPagesToNotebook < ActiveRecord::Migration
def self.up
add_column :notebooks, :pages, :integer
end
def self.down
end
end
perleidžiame migraciją ir klonuojame sturktūrą:
rake db:migrate
rake db:test:clone_structure
vėl testas
#ruby spec/models/notebook_spec.rb
F
1)#'New notebook should have 100 pages' FAILED
expected 100, got 3 (using .eql?)
spec/models/notebook_spec.rb:11:
spec/models/notebook_spec.rb:3:
Finished in 0.093053 seconds
1 example, 1 failure
Testas puikiai indikuoja, kad yra klaida naujos užrašų knygelės puslapių skaičiuje, tad pataisome fixture į:
# spec/fixtures/notebook.yml
my:
id: 1
pages: 100
perleidžiam testą:
#ruby spec/models/notebook_spec.rb
.
Finished in 0.140526 seconds
1 example, 0 failures#
pagaliau praėjome testą!!!Tvarkoj, dabar mums norime, kad išplėšus lapą iš užrašų knygutės, puslapių skaičius sumažėtų vienu. Pridedam:
#spec/models/notebook_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'
describe "New notebook" do
fixtures :notebooks
before(:each) do
@notebook = notebooks(:my)
end
it "should have a 100 pages" do
@notebook.pages.should eql(100)
end
it "should have one page less after removing one page" do
pages_before = @notebook.pages
@notebook.remove_page
@notebook.pages.should eql(pages_before - 1)
end
end
perleidžiam testą:
ruby spec/models/notebook_spec.rb
.F
1)
NoMethodError in 'New notebook should have one page less after removing one page'
undefined method `remove_page' for #<Notebook:0x9a85aa8 @attributes={ id => 1 , pages => 100 }>
spec/models/notebook_spec.rb:16:
spec/models/notebook_spec.rb:3:
Finished in 0.101506 seconds
2 examples, 1 failure
Nepasiskė...neturime tokio metodo, tad sukuriame jį:
#app/models/notebook.rb
class Notebook < ActiveRecord::Base
def remove_page
end
end
Testuojam:
ruby spec/models/notebook_spec.rb
.F
1)
'New notebook should have one page less after removing one page' FAILED
expected 99, got 100 (using .eql?)
spec/models/notebook_spec.rb:17:
spec/models/notebook_spec.rb:3:
Finished in 0.120552 seconds
2 examples, 1 failure
Hmm...puslapių skaičius nesumažėjo, tad ištaisome klaidą:
#app/models/notebook.rb
class Notebook < ActiveRecord::Base
def remove_page
self.pages -= 1
end
end
Testuojam:
# ruby spec/models/notebook_spec.rb
..
Finished in 0.106724 seconds
2 examples, 0 failures
Pagaliau, po ilgos darbo dienos kodas praeina testus, tad galime ramiai eiti namo:) O jei rimtai, tai idėja yra labai paprasta - rašome specifikaciją, o testas pats indikuoja ką programuotuoju reikia padaryti, kad kodas atitiktų specifikaciją. Be to, išlieka ir visi TDD privalumai: užaugus sistemai, gana paprasta daryti pakeitimus, nes testai indikuoja vietas, kurios yra paveikiamos pakeitimo. Nereikia galvoti apie visą sistemą, o spręsti tik mažą specifikacijos reikalavimą, jei tai paliestų kitas sistemos dalis, testai tai parodytų, ir kita. Beje, ar pastebėjote, kad nebuvo naudota naršyklė. Būtent, naudojant šią metodiką nebereikia "mėgėjiškai" ieškoti klaidų.
Tags BDD, rails, rspec, ruby | no comments | no trackbacks