Too Cool for Internet Explorer

Friday, March 21, 2008

Ruby On Rails: Free Training

Sunil Kelkar is a programmer, author, trainer and speaker. A recognized expert in the field of software development with over 15+ years of I.T. experience, Sunil has consulted and trained teams at various companies in India and the US.

He has been involved with Rails for the last two years and Java since 1997.

The first batch of the Free Online Introductory Course on Ruby On Rails starts from 12th April 2008. Registrations are now open.

I'm already registered !

Sunday, March 16, 2008

Ruby from Test::Unit to RSpec part 3 of 3 b

The second part of this article, is about the Spec framework, the one that give Rspec the original name.

After the customer and the developer agree, over a Story describing the business behavior, is time to go on with a more techical specification.

The Spec framework provides RSpec a Domain Specific Language with which you can express executable examples of the expected behaviour of a system.

Thinking about the grading process, we need to describe our process behaviour, so the initial structure will be like this:


require 'spec'
require 'grading'

describe Grading do
...
end

Now, to describe the behaviour, use the folowing sample structure:

1 - To stablish states, of the involved objects.

before(:each) do
(1..9).each { |i| @stack.push i }
@last_item_added = 9
end

2 - RSpec adds "should" and "should_not" methods to any application object.

@student.grade.should ...
post.should_not ...

3 - And a "expectations" set.

be_...(...)
have(...).(...)
raise_error(...)

All together now…

# grading_spec.rb
require 'spec'
require 'grading'

describe Grading do
GradingOverflowError = '123'
before(:each) do
@grading = Grading.new
end

it "should score more then 50 to pass" do
@grading.score(51) == 51
@grading.status('pass').should == 'pass'
end

it "should complain when score more then 100" do
lambda { @grading.score(101) }.should raise_error(GradingOverflowError)
end
end

# grading.rb
class Grading
def score(grade)
if grade > 100
raise '123'
end
end

def status(status)
@status = status
end
end

To run this specification, use this command:

ruby grading_spec.rb --format specdoc

And should get the following result

Grading
- should score more then 50 to pass
- should complain when score more then 100

Finished in 1.219 seconds

2 examples, 0 failures

Now try to add this specification:

it "should get status 'pass' to be assigned to next batch" do
@grading.status('pass') == 'pass'
@grading.assign_batch('pass').should == 3
end

And try to get this result:

Grading
- should score more then 50 to pass
- should complain when score more then 100
- should get status 'pass' to be assigned to next batch

Finished in 0.875 seconds

3 examples, 0 failures

Now start RSpec in practice on the next project.

Sunday, March 2, 2008

Ruby from Test::Unit to RSpec part 3 of 3 a

This would be the last article on this series, but I decided to split this last article about RSpec in two parts:

  • The Story framework (this one)
  • The Spec framework (the next one)

The reason to do that is the importance of this part over the next one. It is near impossible to find useful and complete samples on the Story framework nowadays.

This is the most important part on RSpec, the one that let the customer and the developer to use the same language on understanding the business behavior and specifying the application implementation.

Behaviour Driven Development or BDD, is a thinking evolution on TDD, basically changing from a developer view point to a problem solver view point. Changing from thinking about technical implementation details, to thinking about the problem itself, and how to provide value to the customer through software.

Even with TDD, developers are, most of the time, thinking about technical aspects of the system and often write poor tests.

BDD helps to evolve into a deeper analisys of what the problem really is, which context does the problem apply to, and what are the specifications proof to show that a solution can be demonstrated, forcing developers to focus on the problem and gain a full understanding on it prior to coding.

As system or business analysts, we all agree that would be nice to express customer desires and application specifications, on a common business domain language and vocabulary.

To achieve that RSpec merge Dan North's RBehave into it, since version 1.1, turning RSpec into a BDD, two pieces framework for Ruby, used to write and execute examples of how the application should behave:

  • A Story framework describing application level behavior.
  • A Spec framework describing object level behavior.

Starting with the Story framework, the story narrative should contain a Role, a Feature and a Benefit. To ensure that we use a template like this:

As a (Role)
I want (Feature)
So that (Benefit)

For example:

As a customer
I want to withdraw cash from an ATM,
so that I don't have to wait in line at the bank.

From this starting point and with the agile TDD in mind, we need to specify the acceptance criteria in terms of scenarios. Again, to ensure that we use a template like this:

Given (some initial context, the givens)
When (an event occurs)
Then (ensure some outcomes)

For example:

Scenario 1: Account is in credit
Given the account is in credit
And the card is valid
And the dispenser contains cash
When the customer requests cash
Then ensure the account is debited
And ensure cash is dispensed
And ensure the card is returned

Now for a practical example, imagine a conversation between a developer and a customer:

Developer: describe how to evaluate a student performance.
Customer: Given a specific student, if the student achieves a grade of 50 it should be assigned to a second chance on the summer class. If the student grade is greater than 50, the student pass, if the grade is less then 50, the student fails.

This is a story about a student's grade evaluation.
The text up here above the Story: declaration won't be processed
So you can write whatever you wish, like this kind of comments!

Story: Student grade evaluation

As a school staff member
I want to know if with a specific grade a student has stay, pass or fail
So that I can easily assign (him|her) a new status

Scenario: student grade is just the average
Given a student named 'Monique'
When the student is given a grade of 50
Then the student batch should be same

Scenario: student grade is sufficient to pass
Given a student named 'Michelle'
When the student is given a grade of 60
Then the student batch should be next

Scenario: student grade is insufficient to pass
Given a student named 'Mike'
When the student is given a grade of 40
Then the student batch should be same

Each Given, When and Then is a Step. The Ands (if any), are each the same kind as the previous Step.

Now with the grade.txt file above we need to check it with RSpec.

To do that we just need a simple Ruby program like this.

#!/usr/bin/env ruby
# grade.rb
require 'spec/story'

with_steps_for :grade do
run 'grade.txt'
end

Runing this program, the story with 3 scenarios will be analized, and produce an output with a final conclusion like this:

3 scenarios: 0 succeeded, 0 failed, 3 pending

And nine pending steps alerts, three on each scenario

The scenarios fragments, the Givens, events, and outcomes, are enough to be represented by executable code.

Steps are defined in Ruby like this:

#!/usr/bin/env ruby
# grade_steps.rb
steps_for(:grade) do
Given("a student named '$name'") do |name|
@student = Student.new(name)
end
When("the student is given a grade of $grade") do |grade|
@status = @student.evaluate(grade)
end
Then("the student batch should be $batch") do |status|
@batch = @student.assign_batch(status)
end
end

And objects involved are supported by a class defined on a program like this:

#!/usr/bin/env ruby
# student.rb
class Student
def initialize(name)
end
def evaluate(grade)
end
def assign_batch(status)
end
end

Now we need to include these programs on the check program above, and it will look like this:

#!/usr/bin/env ruby
# grade.rb
require 'spec/story'
require 'grade_steps'
require 'student'

with_steps_for :grade do
run 'grade.txt'
end