Blog
Crafting Rspec Steps with step_eval and DRYing them with a Helper Part 2
By matthias
<-- Part 1
Write an Example for another step
What if we get rid of all those articles and lists?
We'll need another step:
When I destroy all $models
Start by writing the example:
1
2
3
4
5
6
7
8
9
|
describe "When I destroy all $models" do
it "should destroy all Articles" do
class Article
end
Article.should_receive(:destroy_all)
step_eval "When I destroy all articles", :model
end
end
|
Executable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
require 'rubygems'
require 'active_support'
require 'spec'
require 'spec/matchers'
require 'spec/story'
require 'spec/story/step'
# This is the function that performs the step.
def step_eval(stepname, stepgroupname)
type = stepname.split(/\s+/).first.to_s.downcase.to_sym
stepname = stepname.split(/\s+/)[1,100].join(" ")
stepgroup = steps_for stepgroupname
step = stepgroup.find(type, stepname)
if step == nil
raise Spec::Expectations::ExpectationNotMetError.new("Didn't find step: '#{stepname}'")
end
args = step.parse_args(stepname)
step.perform self, *args
end
# Here are the step implementations
steps_for :model do
Given "$count $models" do |count, model|
klass = eval "#{model.singularize.camelize}"
count.to_i.times { klass.create }
end
end
# Here is the spec
describe "Given $count $models" do
it "should create 1 article" do
class Article
end
Article.should_receive(:create).once
step_eval "Given 1 article", :model
end
it "should create 17 lists" do
class List
end
List.should_receive(:create).exactly(17).times
step_eval "Given 17 lists", :model
end
end
describe "When I destroy all $models" do
it "should destroy all Articles" do
class Article
end
Article.should_receive(:destroy_all)
step_eval "When I destroy all articles", :model
end
end
|
Run:
Implement the second step
We have to declare the step in the stepgroup:
1
2
3
4
5
6
7
8
9
10
11
12
|
steps_for :model do
Given "$count $models" do |count, model|
klass = eval "#{model.singularize.camelize}"
count.to_i.times { klass.create }
end
When "I destroy all $models" do |model|
end
end
|
Again the executable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
require 'rubygems'
require 'active_support'
require 'spec'
require 'spec/matchers'
require 'spec/story'
require 'spec/story/step'
# This is the function that performs the step.
def step_eval(stepname, stepgroupname)
type = stepname.split(/\s+/).first.to_s.downcase.to_sym
stepname = stepname.split(/\s+/)[1,100].join(" ")
stepgroup = steps_for stepgroupname
step = stepgroup.find(type, stepname)
if step == nil
raise Spec::Expectations::ExpectationNotMetError.new("Didn't find step: '#{stepname}'")
end
args = step.parse_args(stepname)
step.perform self, *args
end
# Here are the step implementations
steps_for :model do
Given "$count $models" do |count, model|
klass = eval "#{model.singularize.camelize}"
count.to_i.times { klass.create }
end
When "I destroy all $models" do |model|
end
end
# Here is the spec
describe "Given $count $models" do
it "should create 1 article" do
class Article
end
Article.should_receive(:create).once
step_eval "Given 1 article", :model
end
it "should create 17 lists" do
class List
end
List.should_receive(:create).exactly(17).times
step_eval "Given 17 lists", :model
end
end
describe "When I destroy all $models" do
it "should destroy all Articles" do
class Article
end
Article.should_receive(:destroy_all)
step_eval "When I destroy all articles", :model
end
end
|
Run it:

This is obvious again. We need to call List.destroy_all in the step implementation. But this time we know that we will need a more flexible solution and we should reuse the class_from_string_thing from the first step.
DRYing up Steps and speccing a Helper
In our second step we'll need exactly the same first line of code as in the first step. We will only temporary copy and paste it but then write a helper method: (Remember that a step implementation is not an example and thus the clarity-degree may be 20% lower.)
Getting green before we do the next thing
Let's get the example to pass:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
steps_for :model do
Given "$count $models" do |count, model|
klass = eval "#{model.singularize.camelize}"
count.to_i.times { klass.create }
end
When "I destroy all $models" do |model|
klass = eval "#{model.singularize.camelize}"
klass.destroy_all
end
end
|
Executable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
require 'rubygems'
require 'active_support'
require 'spec'
require 'spec/matchers'
require 'spec/story'
require 'spec/story/step'
# This is the function that performs the step.
def step_eval(stepname, stepgroupname)
type = stepname.split(/\s+/).first.to_s.downcase.to_sym
stepname = stepname.split(/\s+/)[1,100].join(" ")
stepgroup = steps_for stepgroupname
step = stepgroup.find(type, stepname)
if step == nil
raise Spec::Expectations::ExpectationNotMetError.new("Didn't find step: '#{stepname}'")
end
args = step.parse_args(stepname)
step.perform self, *args
end
# Here are the step implementations
steps_for :model do
Given "$count $models" do |count, model|
klass = eval "#{model.singularize.camelize}"
count.to_i.times { klass.create }
end
When "I destroy all $models" do |model|
klass = eval "#{model.singularize.camelize}"
klass.destroy_all
end
end
# Here is the spec
describe "Given $count $models" do
it "should create 1 article" do
class Article
end
Article.should_receive(:create).once
step_eval "Given 1 article", :model
end
it "should create 17 lists" do
class List
end
List.should_receive(:create).exactly(17).times
step_eval "Given 17 lists", :model
end
end
describe "When I destroy all $models" do
it "should destroy all Articles" do
class Article
end
Article.should_receive(:destroy_all)
step_eval "When I destroy all articles", :model
end
end
|
Run it:
Green! Let's spec this helper.
We will call our helper module Helper and the method 'class_from_string' and add this infrastructure upfront:
module Helper
def class_from_string(name)
end
end
Writing a first example for the helper:
1
2
3
4
5
6
7
8
|
describe Helper do
describe "#class_from_string" do
it "'article' -> Article" do
class_from_string("article").should == Article
end
end
end
|
Executable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
require 'rubygems'
require 'active_support'
require 'spec'
require 'spec/matchers'
require 'spec/story'
require 'spec/story/step'
# This is the function that performs the step.
def step_eval(stepname, stepgroupname)
type = stepname.split(/\s+/).first.to_s.downcase.to_sym
stepname = stepname.split(/\s+/)[1,100].join(" ")
stepgroup = steps_for stepgroupname
step = stepgroup.find(type, stepname)
if step == nil
raise Spec::Expectations::ExpectationNotMetError.new("Didn't find step: '#{stepname}'")
end
args = step.parse_args(stepname)
step.perform self, *args
end
# Here is the DRYing Helper
module Helper
def class_from_string(name)
end
end
# Here are the step implementations
steps_for :model do
Given "$count $models" do |count, model|
klass = eval "#{model.singularize.camelize}"
count.to_i.times { klass.create }
end
When "I destroy all $models" do |model|
klass = eval "#{model.singularize.camelize}"
klass.destroy_all
end
end
# Here is the spec
describe "Given $count $models" do
it "should create 1 article" do
class Article
end
Article.should_receive(:create).once
step_eval "Given 1 article", :model
end
it "should create 17 lists" do
class List
end
List.should_receive(:create).exactly(17).times
step_eval "Given 17 lists", :model
end
end
describe "When I destroy all $models" do
it "should destroy all Articles" do
class Article
end
Article.should_receive(:destroy_all)
step_eval "When I destroy all articles", :model
end
end
describe Helper do
describe "#class_from_string" do
it "'article' -> Article" do
class_from_string("article").should == Article
end
end
end
|
Run it:

We already have done the implementation in the steps:
1
2
3
4
5
6
|
module Helper
def class_from_string(name)
eval "#{name.singularize.camelize}"
end
end
|
Executable:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
require 'rubygems'
require 'active_support'
require 'spec'
require 'spec/matchers'
require 'spec/story'
require 'spec/story/step'
# This is the function that performs the step.
def step_eval(stepname, stepgroupname)
type = stepname.split(/\s+/).first.to_s.downcase.to_sym
stepname = stepname.split(/\s+/)[1,100].join(" ")
stepgroup = steps_for stepgroupname
step = stepgroup.find(type, stepname)
if step == nil
raise Spec::Expectations::ExpectationNotMetError.new("Didn't find step: '#{stepname}'")
end
args = step.parse_args(stepname)
step.perform self, *args
end
# Here is the DRYing Helper
module Helper
def class_from_string(name)
eval "#{name.singularize.camelize}"
end
end
# Here are the step implementations
steps_for :model do
Given "$count $models" do |count, model|
klass = eval "#{model.singularize.camelize}"
count.to_i.times { klass.create }
end
When "I destroy all $models" do |model|
klass = eval "#{model.singularize.camelize}"
klass.destroy_all
end
end
# Here is the spec
describe "Given $count $models" do
it "should create 1 article" do
class Article
end
Article.should_receive(:create).once
step_eval "Given 1 article", :model
end
it "should create 17 lists" do
class List
end
List.should_receive(:create).exactly(17).times
step_eval "Given 17 lists", :model
end
end
describe "When I destroy all $models" do
it "should destroy all Articles" do
class Article
end
Article.should_receive(:destroy_all)
step_eval "When I destroy all articles", :model
end
end
describe Helper do
describe "#class_from_string" do
it "'article' -> Article" do
class_from_string("article").should == Article
end
end
end |
Run it:
--> Part 3
Sorry, comments are closed for this article.