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