"Elgsena paremtas" programavimas(angl. Behaviour Driven Development). I dalis.

Posted by Saulius Grigaitis 07/05/2007 at 22h20

Sveiki. Pagaliau radau šiek tiek laisvo laiko. Šįkart kiek linksmiau:RSPEC!
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ų.

no comments | no trackbacks

Comments

Trackbacks

Use the following link to trackback from your own site:
http://www.rubyonrails.lt/trackbacks?article_id=12

(leave url/email »)

reCaptcha

   Comment Markup Help Preview comment