cucumber_factory 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.rdoc +12 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/cucumber_factory.gemspec +22 -17
- data/lib/cucumber/factory.rb +109 -0
- data/lib/cucumber/step_mother_ext.rb +36 -0
- data/lib/cucumber_factory.rb +2 -2
- data/spec/factory_spec.rb +0 -111
- data/spec/spec_helper.rb +7 -56
- data/spec/step_mother_ext_spec.rb +29 -0
- data/spec/steps_spec.rb +115 -0
- data/spec/{rcov.opts → support/rcov.opts} +0 -0
- data/spec/{spec.opts → support/spec.opts} +0 -0
- metadata +29 -17
- data/lib/cucumber_factory/factory.rb +0 -108
data/README.rdoc
CHANGED
@@ -43,6 +43,18 @@ You can use named Machinist blueprint such as <tt>Movie.blueprint(:comedy)</tt>
|
|
43
43
|
|
44
44
|
Inherited factory_girl factories are not supported yet.
|
45
45
|
|
46
|
+
== Overriding factory steps
|
47
|
+
|
48
|
+
If you want to override a factory step with your own version, just do so:
|
49
|
+
|
50
|
+
Given /^there is a movie with good actors$/ do
|
51
|
+
movie = Movie.make
|
52
|
+
movie.actors << Actor.make(:name => 'Clive Owen')
|
53
|
+
movie.actors << Actor.make(:name => 'Denzel Washington')
|
54
|
+
end
|
55
|
+
|
56
|
+
Custom steps will always be preferred over factory steps. Also Cucumber will not raise a warning about ambiguous steps if the only other matching step is a factory step.
|
57
|
+
|
46
58
|
=== Credits
|
47
59
|
|
48
60
|
Henning Koch
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.5.0
|
data/cucumber_factory.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{cucumber_factory}
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "1.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Henning Koch"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-06-20}
|
13
13
|
s.description = %q{Cucumber Factory allows you to create ActiveRecord models from your Cucumber features without writing step definitions for each model.}
|
14
14
|
s.email = %q{github@makandra.de}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -22,8 +22,9 @@ Gem::Specification.new do |s|
|
|
22
22
|
"Rakefile",
|
23
23
|
"VERSION",
|
24
24
|
"cucumber_factory.gemspec",
|
25
|
+
"lib/cucumber/factory.rb",
|
26
|
+
"lib/cucumber/step_mother_ext.rb",
|
25
27
|
"lib/cucumber_factory.rb",
|
26
|
-
"lib/cucumber_factory/factory.rb",
|
27
28
|
"spec/app_root/app/controllers/application_controller.rb",
|
28
29
|
"spec/app_root/app/models/job_offer.rb",
|
29
30
|
"spec/app_root/app/models/machinist_model.rb",
|
@@ -48,38 +49,42 @@ Gem::Specification.new do |s|
|
|
48
49
|
"spec/app_root/log/.gitignore",
|
49
50
|
"spec/app_root/script/console",
|
50
51
|
"spec/factory_spec.rb",
|
51
|
-
"spec/
|
52
|
-
"spec/
|
53
|
-
"spec/
|
52
|
+
"spec/spec_helper.rb",
|
53
|
+
"spec/step_mother_ext_spec.rb",
|
54
|
+
"spec/steps_spec.rb",
|
55
|
+
"spec/support/rcov.opts",
|
56
|
+
"spec/support/spec.opts"
|
54
57
|
]
|
55
58
|
s.homepage = %q{http://github.com/makandra/cucumber_factory}
|
56
59
|
s.rdoc_options = ["--charset=UTF-8"]
|
57
60
|
s.require_paths = ["lib"]
|
58
|
-
s.rubygems_version = %q{1.3.
|
61
|
+
s.rubygems_version = %q{1.3.6}
|
59
62
|
s.summary = %q{Create records from Cucumber features without writing step definitions.}
|
60
63
|
s.test_files = [
|
61
|
-
"spec/app_root/app/
|
62
|
-
"spec/app_root/app/models/movie.rb",
|
64
|
+
"spec/app_root/app/controllers/application_controller.rb",
|
63
65
|
"spec/app_root/app/models/job_offer.rb",
|
64
|
-
"spec/app_root/app/models/payment.rb",
|
65
66
|
"spec/app_root/app/models/machinist_model.rb",
|
67
|
+
"spec/app_root/app/models/movie.rb",
|
68
|
+
"spec/app_root/app/models/opera.rb",
|
69
|
+
"spec/app_root/app/models/payment.rb",
|
66
70
|
"spec/app_root/app/models/plain_ruby_class.rb",
|
67
71
|
"spec/app_root/app/models/user.rb",
|
68
|
-
"spec/app_root/
|
72
|
+
"spec/app_root/config/boot.rb",
|
69
73
|
"spec/app_root/config/environment.rb",
|
74
|
+
"spec/app_root/config/environments/in_memory.rb",
|
70
75
|
"spec/app_root/config/environments/mysql.rb",
|
71
76
|
"spec/app_root/config/environments/postgresql.rb",
|
72
|
-
"spec/app_root/config/environments/sqlite3.rb",
|
73
|
-
"spec/app_root/config/environments/in_memory.rb",
|
74
77
|
"spec/app_root/config/environments/sqlite.rb",
|
75
|
-
"spec/app_root/config/
|
78
|
+
"spec/app_root/config/environments/sqlite3.rb",
|
76
79
|
"spec/app_root/config/routes.rb",
|
77
|
-
"spec/app_root/db/migrate/002_create_users.rb",
|
78
80
|
"spec/app_root/db/migrate/001_create_movies.rb",
|
81
|
+
"spec/app_root/db/migrate/002_create_users.rb",
|
79
82
|
"spec/app_root/db/migrate/003_create_payments.rb",
|
80
83
|
"spec/app_root/lib/console_with_fixtures.rb",
|
81
|
-
"spec/
|
82
|
-
"spec/
|
84
|
+
"spec/spec_helper.rb",
|
85
|
+
"spec/step_mother_ext_spec.rb",
|
86
|
+
"spec/steps_spec.rb",
|
87
|
+
"spec/factory_spec.rb"
|
83
88
|
]
|
84
89
|
|
85
90
|
if s.respond_to? :specification_version then
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Cucumber
|
2
|
+
module Factory
|
3
|
+
class << self
|
4
|
+
|
5
|
+
# List of Cucumber step definitions created by #add_steps
|
6
|
+
attr_reader :step_definitions
|
7
|
+
|
8
|
+
def add_steps(dsl)
|
9
|
+
@step_definitions = []
|
10
|
+
steps.each do |step|
|
11
|
+
@step_definitions << (dsl.instance_eval do
|
12
|
+
Given(step[:pattern], &step[:action])
|
13
|
+
end)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def steps
|
18
|
+
[ { :pattern => /^"([^\"]*)" is an? (.+?)( \(.+?\))?( with the .+?)?$/,
|
19
|
+
:action => lambda { |name, raw_model, raw_variant, raw_attributes| Cucumber::Factory.parse_named_creation(self, name, raw_model, raw_variant, raw_attributes) } },
|
20
|
+
{ :pattern => /^there is an? (.+?)( \(.+?\))?( with the .+?)?$/,
|
21
|
+
:action => lambda { |raw_model, raw_variant, raw_attributes| Cucumber::Factory.parse_creation(self, raw_model, raw_variant, raw_attributes) } } ]
|
22
|
+
end
|
23
|
+
|
24
|
+
# # Emulate Cucumber step matching for specs
|
25
|
+
# def parse(world, command)
|
26
|
+
# command = command.sub(/^When |Given |Then /, "")
|
27
|
+
# steps.each do |step|
|
28
|
+
# match = step[:pattern].match(command)
|
29
|
+
# if match
|
30
|
+
# step[:action].bind(world).call(*match.captures)
|
31
|
+
# return
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# raise "No step definition for: #{command}"
|
35
|
+
# end
|
36
|
+
|
37
|
+
def parse_named_creation(world, name, raw_model, raw_variant, raw_attributes)
|
38
|
+
record = parse_creation(world, raw_model, raw_variant, raw_attributes)
|
39
|
+
variable = variable_name_from_prose(name)
|
40
|
+
world.instance_variable_set variable, record
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_creation(world, raw_model, raw_variant, raw_attributes)
|
44
|
+
model_class = model_class_from_prose(raw_model)
|
45
|
+
attributes = {}
|
46
|
+
if raw_attributes.present? && raw_attributes.strip.present?
|
47
|
+
raw_attributes.scan(/(the|and|with| )+(.*?) ("([^\"]*)"|above)/).each do |fragment|
|
48
|
+
value = nil
|
49
|
+
attribute = fragment[1].downcase.gsub(" ", "_").to_sym
|
50
|
+
value_type = fragment[2] # 'above' or a quoted string
|
51
|
+
value = fragment[3]
|
52
|
+
association = model_class.reflect_on_association(attribute) if model_class.respond_to?(:reflect_on_association)
|
53
|
+
if association.present?
|
54
|
+
if value_type == "above"
|
55
|
+
# Don't use class.last, in sqlite that is not always the last inserted element
|
56
|
+
value = association.klass.find(:last, :order => "id") or raise "There is no last #{attribute}"
|
57
|
+
else
|
58
|
+
value = world.instance_variable_get(variable_name_from_prose(value))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
attributes[attribute] = value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
variant = raw_variant.present? && /\((.*?)\)/.match(raw_variant)[1].downcase.gsub(" ", "_")
|
65
|
+
create_record(model_class, variant, attributes)
|
66
|
+
end
|
67
|
+
|
68
|
+
def model_class_from_prose(prose)
|
69
|
+
# don't use \w which depends on the system locale
|
70
|
+
prose.gsub(/[^A-Za-z0-9_]+/, "_").camelize.constantize
|
71
|
+
end
|
72
|
+
|
73
|
+
def variable_name_from_prose(prose)
|
74
|
+
# don't use \w which depends on the system locale
|
75
|
+
name = prose.downcase.gsub(/[^A-Za-z0-9_]+/, '_')
|
76
|
+
name = name.gsub(/^_+/, '').gsub(/_+$/, '')
|
77
|
+
name = "_#{name}" unless name.length >= 0 && name =~ /^[a-z]/
|
78
|
+
:"@#{name}"
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def factory_girl_factory_name(model_class)
|
84
|
+
model_class.to_s.underscore.to_sym
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_record(model_class, variant, attributes)
|
88
|
+
factory_name = factory_girl_factory_name(model_class)
|
89
|
+
if defined?(::Factory) && factory = ::Factory.factories[factory_name]
|
90
|
+
::Factory.create(factory_name, attributes)
|
91
|
+
elsif model_class.respond_to?(:make) # Machinist blueprint
|
92
|
+
if variant.present?
|
93
|
+
model_class.make(variant.to_sym, attributes)
|
94
|
+
else
|
95
|
+
model_class.make(attributes)
|
96
|
+
end
|
97
|
+
elsif model_class.respond_to?(:create!) # Plain ActiveRecord
|
98
|
+
model = model_class.new
|
99
|
+
model.send(:attributes=, attributes, false) # ignore attr_accessible
|
100
|
+
model.save!
|
101
|
+
model
|
102
|
+
else
|
103
|
+
model_class.new(attributes)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'cucumber/step_mother'
|
2
|
+
|
3
|
+
module Cucumber
|
4
|
+
|
5
|
+
class Ambiguous
|
6
|
+
|
7
|
+
attr_reader :matches
|
8
|
+
|
9
|
+
def initialize_with_remembering_matches(step_name, matches, *args)
|
10
|
+
@matches = matches
|
11
|
+
initialize_without_remembering_matches(step_name, matches, *args)
|
12
|
+
end
|
13
|
+
|
14
|
+
alias_method_chain :initialize, :remembering_matches
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class StepMother
|
19
|
+
|
20
|
+
def step_match_with_factory_priority(*args)
|
21
|
+
step_match_without_factory_priority(*args)
|
22
|
+
rescue Ambiguous => e
|
23
|
+
matched_definitions = e.matches.collect(&:step_definition)
|
24
|
+
if matched_definitions.size == 2 && (Cucumber::Factory.step_definitions & matched_definitions).any?
|
25
|
+
(matched_definitions - Cucumber::Factory.step_definitions).first
|
26
|
+
else
|
27
|
+
raise
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method_chain :step_match, :factory_priority
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
data/lib/cucumber_factory.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
require '
|
1
|
+
require 'cucumber/factory'
|
2
|
+
require 'cucumber/step_mother_ext'
|
data/spec/factory_spec.rb
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper'
|
2
2
|
|
3
|
-
class Factory # for factory_girl compatibility spec
|
4
|
-
def self.factories
|
5
|
-
{}
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
3
|
describe Cucumber::Factory do
|
10
4
|
|
11
5
|
describe 'add_steps' do
|
@@ -40,110 +34,5 @@ describe Cucumber::Factory do
|
|
40
34
|
end
|
41
35
|
|
42
36
|
end
|
43
|
-
|
44
|
-
describe 'parse' do
|
45
|
-
|
46
|
-
before(:each) do
|
47
|
-
@world = Object.new
|
48
|
-
end
|
49
|
-
|
50
|
-
it "should create ActiveRecord models by calling #new and #save!" do
|
51
|
-
movie = Movie.new
|
52
|
-
Movie.should_receive(:new).with().and_return(movie)
|
53
|
-
movie.should_receive(:save!)
|
54
|
-
Cucumber::Factory.parse(@world, "Given there is a movie")
|
55
|
-
end
|
56
|
-
|
57
|
-
it "should create models that have a machinist blueprint by calling #make" do
|
58
|
-
MachinistModel.should_receive(:make).with({ :attribute => "foo"})
|
59
|
-
Cucumber::Factory.parse(@world, 'Given there is a machinist model with the attribute "foo"')
|
60
|
-
end
|
61
|
-
|
62
|
-
it "should be able to invoke machinist blueprint variants" do
|
63
|
-
MachinistModel.should_receive(:make).with(:variant, { :attribute => "foo"})
|
64
|
-
Cucumber::Factory.parse(@world, 'Given there is a machinist model (variant) with the attribute "foo"')
|
65
|
-
end
|
66
|
-
|
67
|
-
it "should be able to invoke machinist blueprint variants containing spaces or uppercase characters in prose" do
|
68
|
-
MachinistModel.should_receive(:make).with(:variant_mark_two, { :attribute => "foo"})
|
69
|
-
Cucumber::Factory.parse(@world, 'Given there is a machinist model (Variant Mark Two) with the attribute "foo"')
|
70
|
-
end
|
71
|
-
|
72
|
-
it "should create models that have a factory_girl factory by calling #Factory.make(:model_name)" do
|
73
|
-
Factory.should_receive(:factories).with().and_return({ :job_offer => :job_offer_factory }) # Fake factory look up in factory_girl
|
74
|
-
Factory.should_receive(:create).with(:job_offer, { :title => "Awesome job" })
|
75
|
-
Cucumber::Factory.parse(@world, 'Given there is a job offer with the title "Awesome job"')
|
76
|
-
end
|
77
|
-
|
78
|
-
it "should instantiate plain ruby classes by calling #new" do
|
79
|
-
PlainRubyClass.should_receive(:new).with({})
|
80
|
-
Cucumber::Factory.parse(@world, "Given there is a plain ruby class")
|
81
|
-
end
|
82
|
-
|
83
|
-
it "should instantiate classes with multiple words in their name" do
|
84
|
-
JobOffer.should_receive(:new).with({})
|
85
|
-
Cucumber::Factory.parse(@world, "Given there is a job offer")
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should instantiate classes with uppercase characters in their name" do
|
89
|
-
user = User.new
|
90
|
-
User.should_receive(:new).and_return(user)
|
91
|
-
Cucumber::Factory.parse(@world, "Given there is a User")
|
92
|
-
end
|
93
|
-
|
94
|
-
it "should allow either 'a' or 'an' for the article" do
|
95
|
-
Opera.should_receive(:new).with({})
|
96
|
-
Cucumber::Factory.parse(@world, "Given there is an opera")
|
97
|
-
end
|
98
|
-
|
99
|
-
it "should create records with attributes" do
|
100
|
-
movie = Movie.new
|
101
|
-
Movie.stub(:new => movie)
|
102
|
-
movie.should_receive(:"attributes=").with({ :title => "Sunshine", :year => "2007" }, false)
|
103
|
-
Cucumber::Factory.parse(@world, 'Given there is a movie with the title "Sunshine" and the year "2007"')
|
104
|
-
end
|
105
|
-
|
106
|
-
it "should create records with attributes containing spaces" do
|
107
|
-
movie = Movie.new
|
108
|
-
Movie.stub(:new => movie)
|
109
|
-
movie.should_receive(:"attributes=").with({ :box_office_result => "99999999" }, false)
|
110
|
-
Cucumber::Factory.parse(@world, 'Given there is a movie with the box office result "99999999"')
|
111
|
-
end
|
112
|
-
|
113
|
-
it "should create records with attributes containing uppercase characters" do
|
114
|
-
user = User.new
|
115
|
-
User.stub(:new => user)
|
116
|
-
user.should_receive(:"attributes=").with({ :name => "Susanne" }, false)
|
117
|
-
Cucumber::Factory.parse(@world, 'Given there is a User with the Name "Susanne"')
|
118
|
-
end
|
119
|
-
|
120
|
-
it "should override attr_accessible protection" do
|
121
|
-
Cucumber::Factory.parse(@world, 'Given there is a payment with the amount "120" and the comment "Thanks for lending"')
|
122
|
-
payment = Payment.last
|
123
|
-
payment.amount.should == 120
|
124
|
-
payment.comment.should == 'Thanks for lending'
|
125
|
-
end
|
126
|
-
|
127
|
-
it "should set instance variables in the world" do
|
128
|
-
Cucumber::Factory.parse(@world, 'Given "Sunshine" is a movie with the title "Sunshine" and the year "2007"')
|
129
|
-
@world.instance_variable_get(:'@sunshine').title.should == "Sunshine"
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should understand pointers to instance variables" do
|
133
|
-
Cucumber::Factory.parse(@world, 'Given "Before Sunrise" is a movie with the title "Before Sunrise"')
|
134
|
-
Cucumber::Factory.parse(@world, 'Given "Before Sunset" is a movie with the title "Before Sunset" and the prequel "Before Sunrise"')
|
135
|
-
@world.instance_variable_get(:'@before_sunset').prequel.title.should == "Before Sunrise"
|
136
|
-
end
|
137
|
-
|
138
|
-
it "should allow to point to a previously created record through 'above'" do
|
139
|
-
Cucumber::Factory.parse(@world, 'Given there is a user with the name "Jane"')
|
140
|
-
Cucumber::Factory.parse(@world, 'Given there is a movie with the title "Before Sunrise"')
|
141
|
-
Cucumber::Factory.parse(@world, 'Given there is a movie with the title "Before Sunset" and the reviewer above and the prequel above')
|
142
|
-
@before_sunset = Movie.find_by_title!("Before Sunset")
|
143
|
-
@before_sunset.prequel.title.should == "Before Sunrise"
|
144
|
-
@before_sunset.reviewer.name.should == "Jane"
|
145
|
-
end
|
146
|
-
|
147
|
-
end
|
148
37
|
|
149
38
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
$: << File.join(File.dirname(__FILE__), "/../lib" )
|
2
|
-
#require 'lib/cucumber_factory'
|
3
|
-
# Dir["spec/fixtures/**/*.rb"].each {|f| require f}
|
4
|
-
|
5
|
-
|
6
2
|
|
7
3
|
# Set the default environment to sqlite3's in_memory database
|
8
4
|
ENV['RAILS_ENV'] ||= 'in_memory'
|
@@ -23,55 +19,10 @@ Spec::Runner.configure do |config|
|
|
23
19
|
config.use_instantiated_fixtures = false
|
24
20
|
end
|
25
21
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
#
|
34
|
-
## Requires supporting files with custom matchers and macros, etc,
|
35
|
-
## in ./support/ and its subdirectories.
|
36
|
-
#Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
37
|
-
#
|
38
|
-
#Spec::Runner.configure do |config|
|
39
|
-
# # If you're not using ActiveRecord you should remove these
|
40
|
-
# # lines, delete config/database.yml and disable :active_record
|
41
|
-
# # in your config/boot.rb
|
42
|
-
# config.use_transactional_fixtures = true
|
43
|
-
# config.use_instantiated_fixtures = false
|
44
|
-
# config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
|
45
|
-
#
|
46
|
-
# # == Fixtures
|
47
|
-
# #
|
48
|
-
# # You can declare fixtures for each example_group like this:
|
49
|
-
# # describe "...." do
|
50
|
-
# # fixtures :table_a, :table_b
|
51
|
-
# #
|
52
|
-
# # Alternatively, if you prefer to declare them only once, you can
|
53
|
-
# # do so right here. Just uncomment the next line and replace the fixture
|
54
|
-
# # names with your fixtures.
|
55
|
-
# #
|
56
|
-
# # config.global_fixtures = :table_a, :table_b
|
57
|
-
# #
|
58
|
-
# # If you declare global fixtures, be aware that they will be declared
|
59
|
-
# # for all of your examples, even those that don't use them.
|
60
|
-
# #
|
61
|
-
# # You can also declare which fixtures to use (for example fixtures for test/fixtures):
|
62
|
-
# #
|
63
|
-
# # config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
|
64
|
-
# #
|
65
|
-
# # == Mock Framework
|
66
|
-
# #
|
67
|
-
# # RSpec uses it's own mocking framework by default. If you prefer to
|
68
|
-
# # use mocha, flexmock or RR, uncomment the appropriate line:
|
69
|
-
# #
|
70
|
-
# # config.mock_with :mocha
|
71
|
-
# # config.mock_with :flexmock
|
72
|
-
# # config.mock_with :rr
|
73
|
-
# #
|
74
|
-
# # == Notes
|
75
|
-
# #
|
76
|
-
# # For more information take a look at Spec::Runner::Configuration and Spec::Runner
|
77
|
-
#end
|
22
|
+
def prepare_cucumber_example
|
23
|
+
@step_mother = Cucumber::StepMother.new
|
24
|
+
@language = @step_mother.load_programming_language('rb')
|
25
|
+
@dsl = Object.new
|
26
|
+
@dsl.extend(Cucumber::RbSupport::RbDsl)
|
27
|
+
Cucumber::Factory.add_steps(@dsl)
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
require 'cucumber'
|
4
|
+
require 'cucumber/rb_support/rb_language'
|
5
|
+
|
6
|
+
describe Cucumber::StepMother, 'extended with cucumber_factory' do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
prepare_cucumber_example
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'step_match' do
|
13
|
+
|
14
|
+
it "should not raise an ambiguous step error and return the user step if the only other matching step is a factory step" do
|
15
|
+
user_step = @dsl.Given(/^there is a movie with a funny tone/){}
|
16
|
+
@step_mother.step_match('there is a movie with a funny tone').should == user_step
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should still raise an ambiguous step error if more than two non-factory steps match" do
|
20
|
+
@dsl.Given(/^there is a movie with (.*?) tone/){}
|
21
|
+
@dsl.Given(/^there is a movie with a funny tone/){}
|
22
|
+
expect do
|
23
|
+
@step_mother.step_match('there is a movie with a funny tone')
|
24
|
+
end.to raise_error(Cucumber::Ambiguous)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
data/spec/steps_spec.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
require 'cucumber'
|
4
|
+
require 'cucumber/rb_support/rb_language'
|
5
|
+
|
6
|
+
class Factory # for factory_girl compatibility spec
|
7
|
+
def self.factories
|
8
|
+
{}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'steps provided by cucumber_factory' do
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
prepare_cucumber_example
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should create ActiveRecord models by calling #new and #save!" do
|
19
|
+
movie = Movie.new
|
20
|
+
Movie.should_receive(:new).with().and_return(movie)
|
21
|
+
movie.should_receive(:save!)
|
22
|
+
@step_mother.invoke("there is a movie")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should create models that have a machinist blueprint by calling #make" do
|
26
|
+
MachinistModel.should_receive(:make).with({ :attribute => "foo"})
|
27
|
+
@step_mother.invoke('there is a machinist model with the attribute "foo"')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should be able to invoke machinist blueprint variants" do
|
31
|
+
MachinistModel.should_receive(:make).with(:variant, { :attribute => "foo"})
|
32
|
+
@step_mother.invoke('there is a machinist model (variant) with the attribute "foo"')
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be able to invoke machinist blueprint variants containing spaces or uppercase characters in prose" do
|
36
|
+
MachinistModel.should_receive(:make).with(:variant_mark_two, { :attribute => "foo"})
|
37
|
+
@step_mother.invoke('there is a machinist model (Variant Mark Two) with the attribute "foo"')
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should create models that have a factory_girl factory by calling #Factory.make(:model_name)" do
|
41
|
+
Factory.should_receive(:factories).with().and_return({ :job_offer => :job_offer_factory }) # Fake factory look up in factory_girl
|
42
|
+
Factory.should_receive(:create).with(:job_offer, { :title => "Awesome job" })
|
43
|
+
@step_mother.invoke('there is a job offer with the title "Awesome job"')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should instantiate plain ruby classes by calling #new" do
|
47
|
+
PlainRubyClass.should_receive(:new).with({})
|
48
|
+
@step_mother.invoke("there is a plain ruby class")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should instantiate classes with multiple words in their name" do
|
52
|
+
JobOffer.should_receive(:new).with({})
|
53
|
+
@step_mother.invoke("there is a job offer")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should instantiate classes with uppercase characters in their name" do
|
57
|
+
user = User.new
|
58
|
+
User.should_receive(:new).and_return(user)
|
59
|
+
@step_mother.invoke("there is a User")
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should allow either 'a' or 'an' for the article" do
|
63
|
+
Opera.should_receive(:new).with({})
|
64
|
+
@step_mother.invoke("there is an opera")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should create records with attributes" do
|
68
|
+
movie = Movie.new
|
69
|
+
Movie.stub(:new => movie)
|
70
|
+
movie.should_receive(:"attributes=").with({ :title => "Sunshine", :year => "2007" }, false)
|
71
|
+
@step_mother.invoke('there is a movie with the title "Sunshine" and the year "2007"')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should create records with attributes containing spaces" do
|
75
|
+
movie = Movie.new
|
76
|
+
Movie.stub(:new => movie)
|
77
|
+
movie.should_receive(:"attributes=").with({ :box_office_result => "99999999" }, false)
|
78
|
+
@step_mother.invoke('there is a movie with the box office result "99999999"')
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should create records with attributes containing uppercase characters" do
|
82
|
+
user = User.new
|
83
|
+
User.stub(:new => user)
|
84
|
+
user.should_receive(:"attributes=").with({ :name => "Susanne" }, false)
|
85
|
+
@step_mother.invoke('there is a User with the Name "Susanne"')
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should override attr_accessible protection" do
|
89
|
+
@step_mother.invoke('there is a payment with the amount "120" and the comment "Thanks for lending"')
|
90
|
+
payment = Payment.last
|
91
|
+
payment.amount.should == 120
|
92
|
+
payment.comment.should == 'Thanks for lending'
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should set instance variables in the world" do
|
96
|
+
@step_mother.invoke('"Sunshine" is a movie with the title "Sunshine" and the year "2007"')
|
97
|
+
@language.current_world.instance_variable_get(:'@sunshine').title.should == "Sunshine"
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should understand pointers to instance variables" do
|
101
|
+
@step_mother.invoke('"Before Sunrise" is a movie with the title "Before Sunrise"')
|
102
|
+
@step_mother.invoke('"Before Sunset" is a movie with the title "Before Sunset" and the prequel "Before Sunrise"')
|
103
|
+
@language.current_world.instance_variable_get(:'@before_sunset').prequel.title.should == "Before Sunrise"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should allow to point to a previously created record through 'above'" do
|
107
|
+
@step_mother.invoke('there is a user with the name "Jane"')
|
108
|
+
@step_mother.invoke('there is a movie with the title "Before Sunrise"')
|
109
|
+
@step_mother.invoke('there is a movie with the title "Before Sunset" and the reviewer above and the prequel above')
|
110
|
+
@before_sunset = Movie.find_by_title!("Before Sunset")
|
111
|
+
@before_sunset.prequel.title.should == "Before Sunrise"
|
112
|
+
@before_sunset.reviewer.name.should == "Jane"
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
File without changes
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cucumber_factory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 5
|
8
|
+
- 0
|
9
|
+
version: 1.5.0
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Henning Koch
|
@@ -9,7 +14,7 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-06-20 00:00:00 +02:00
|
13
18
|
default_executable:
|
14
19
|
dependencies: []
|
15
20
|
|
@@ -28,8 +33,9 @@ files:
|
|
28
33
|
- Rakefile
|
29
34
|
- VERSION
|
30
35
|
- cucumber_factory.gemspec
|
36
|
+
- lib/cucumber/factory.rb
|
37
|
+
- lib/cucumber/step_mother_ext.rb
|
31
38
|
- lib/cucumber_factory.rb
|
32
|
-
- lib/cucumber_factory/factory.rb
|
33
39
|
- spec/app_root/app/controllers/application_controller.rb
|
34
40
|
- spec/app_root/app/models/job_offer.rb
|
35
41
|
- spec/app_root/app/models/machinist_model.rb
|
@@ -54,9 +60,11 @@ files:
|
|
54
60
|
- spec/app_root/log/.gitignore
|
55
61
|
- spec/app_root/script/console
|
56
62
|
- spec/factory_spec.rb
|
57
|
-
- spec/rcov.opts
|
58
|
-
- spec/spec.opts
|
59
63
|
- spec/spec_helper.rb
|
64
|
+
- spec/step_mother_ext_spec.rb
|
65
|
+
- spec/steps_spec.rb
|
66
|
+
- spec/support/rcov.opts
|
67
|
+
- spec/support/spec.opts
|
60
68
|
has_rdoc: true
|
61
69
|
homepage: http://github.com/makandra/cucumber_factory
|
62
70
|
licenses: []
|
@@ -70,41 +78,45 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
70
78
|
requirements:
|
71
79
|
- - ">="
|
72
80
|
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 0
|
73
83
|
version: "0"
|
74
|
-
version:
|
75
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
85
|
requirements:
|
77
86
|
- - ">="
|
78
87
|
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
79
90
|
version: "0"
|
80
|
-
version:
|
81
91
|
requirements: []
|
82
92
|
|
83
93
|
rubyforge_project:
|
84
|
-
rubygems_version: 1.3.
|
94
|
+
rubygems_version: 1.3.6
|
85
95
|
signing_key:
|
86
96
|
specification_version: 3
|
87
97
|
summary: Create records from Cucumber features without writing step definitions.
|
88
98
|
test_files:
|
89
|
-
- spec/app_root/app/
|
90
|
-
- spec/app_root/app/models/movie.rb
|
99
|
+
- spec/app_root/app/controllers/application_controller.rb
|
91
100
|
- spec/app_root/app/models/job_offer.rb
|
92
|
-
- spec/app_root/app/models/payment.rb
|
93
101
|
- spec/app_root/app/models/machinist_model.rb
|
102
|
+
- spec/app_root/app/models/movie.rb
|
103
|
+
- spec/app_root/app/models/opera.rb
|
104
|
+
- spec/app_root/app/models/payment.rb
|
94
105
|
- spec/app_root/app/models/plain_ruby_class.rb
|
95
106
|
- spec/app_root/app/models/user.rb
|
96
|
-
- spec/app_root/
|
107
|
+
- spec/app_root/config/boot.rb
|
97
108
|
- spec/app_root/config/environment.rb
|
109
|
+
- spec/app_root/config/environments/in_memory.rb
|
98
110
|
- spec/app_root/config/environments/mysql.rb
|
99
111
|
- spec/app_root/config/environments/postgresql.rb
|
100
|
-
- spec/app_root/config/environments/sqlite3.rb
|
101
|
-
- spec/app_root/config/environments/in_memory.rb
|
102
112
|
- spec/app_root/config/environments/sqlite.rb
|
103
|
-
- spec/app_root/config/
|
113
|
+
- spec/app_root/config/environments/sqlite3.rb
|
104
114
|
- spec/app_root/config/routes.rb
|
105
|
-
- spec/app_root/db/migrate/002_create_users.rb
|
106
115
|
- spec/app_root/db/migrate/001_create_movies.rb
|
116
|
+
- spec/app_root/db/migrate/002_create_users.rb
|
107
117
|
- spec/app_root/db/migrate/003_create_payments.rb
|
108
118
|
- spec/app_root/lib/console_with_fixtures.rb
|
109
|
-
- spec/factory_spec.rb
|
110
119
|
- spec/spec_helper.rb
|
120
|
+
- spec/step_mother_ext_spec.rb
|
121
|
+
- spec/steps_spec.rb
|
122
|
+
- spec/factory_spec.rb
|
@@ -1,108 +0,0 @@
|
|
1
|
-
module Cucumber
|
2
|
-
module Factory
|
3
|
-
|
4
|
-
def self.add_steps(world)
|
5
|
-
steps.each do |step|
|
6
|
-
world.instance_eval do
|
7
|
-
Given(step[0], &step[1].bind(world))
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.steps
|
13
|
-
[
|
14
|
-
[
|
15
|
-
/^"([^\"]*)" is an? (.+?)( \(.+?\))?( with the .+?)?$/,
|
16
|
-
lambda { |name, raw_model, raw_variant, raw_attributes| Cucumber::Factory.parse_named_creation(self, name, raw_model, raw_variant, raw_attributes) }
|
17
|
-
],
|
18
|
-
[
|
19
|
-
/^there is an? (.+?)( \(.+?\))?( with the .+?)?$/,
|
20
|
-
lambda { |raw_model, raw_variant, raw_attributes| Cucumber::Factory.parse_creation(self, raw_model, raw_variant, raw_attributes) }
|
21
|
-
]
|
22
|
-
]
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.parse(world, command)
|
26
|
-
command = command.sub(/^When |Given |Then /, "")
|
27
|
-
steps.each do |step|
|
28
|
-
match = step[0].match(command)
|
29
|
-
if match
|
30
|
-
step[1].bind(world).call(*match.captures)
|
31
|
-
return
|
32
|
-
end
|
33
|
-
end
|
34
|
-
raise "No step definition for: #{command}"
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.parse_named_creation(world, name, raw_model, raw_variant, raw_attributes)
|
38
|
-
record = parse_creation(world, raw_model, raw_variant, raw_attributes)
|
39
|
-
variable = variable_name_from_prose(name)
|
40
|
-
world.instance_variable_set variable, record
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.parse_creation(world, raw_model, raw_variant, raw_attributes)
|
44
|
-
model_class = model_class_from_prose(raw_model)
|
45
|
-
attributes = {}
|
46
|
-
if raw_attributes.present? && raw_attributes.strip.present?
|
47
|
-
raw_attributes.scan(/(the|and|with| )+(.*?) ("([^\"]*)"|above)/).each do |fragment|
|
48
|
-
value = nil
|
49
|
-
attribute = fragment[1].downcase.gsub(" ", "_").to_sym
|
50
|
-
value_type = fragment[2] # 'above' or a quoted string
|
51
|
-
value = fragment[3]
|
52
|
-
association = model_class.reflect_on_association(attribute) if model_class.respond_to?(:reflect_on_association)
|
53
|
-
if association.present?
|
54
|
-
if value_type == "above"
|
55
|
-
# Don't use class.last, in sqlite that is not always the last inserted element
|
56
|
-
value = association.klass.find(:last, :order => "id") or raise "There is no last #{attribute}"
|
57
|
-
else
|
58
|
-
value = world.instance_variable_get(variable_name_from_prose(value))
|
59
|
-
end
|
60
|
-
end
|
61
|
-
attributes[attribute] = value
|
62
|
-
end
|
63
|
-
end
|
64
|
-
variant = raw_variant.present? && /\((.*?)\)/.match(raw_variant)[1].downcase.gsub(" ", "_")
|
65
|
-
create_record(model_class, variant, attributes)
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.model_class_from_prose(prose)
|
69
|
-
# don't use \w which depends on the system locale
|
70
|
-
prose.gsub(/[^A-Za-z0-9_]+/, "_").camelize.constantize
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.variable_name_from_prose(prose)
|
74
|
-
# don't use \w which depends on the system locale
|
75
|
-
name = prose.downcase.gsub(/[^A-Za-z0-9_]+/, '_')
|
76
|
-
name = name.gsub(/^_+/, '').gsub(/_+$/, '')
|
77
|
-
name = "_#{name}" unless name.length >= 0 && name =~ /^[a-z]/
|
78
|
-
:"@#{name}"
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def self.factory_girl_factory_name(model_class)
|
84
|
-
model_class.to_s.underscore.to_sym
|
85
|
-
end
|
86
|
-
|
87
|
-
def self.create_record(model_class, variant, attributes)
|
88
|
-
factory_name = factory_girl_factory_name(model_class)
|
89
|
-
if defined?(::Factory) && factory = ::Factory.factories[factory_name]
|
90
|
-
::Factory.create(factory_name, attributes)
|
91
|
-
elsif model_class.respond_to?(:make) # Machinist blueprint
|
92
|
-
if variant.present?
|
93
|
-
model_class.make(variant.to_sym, attributes)
|
94
|
-
else
|
95
|
-
model_class.make(attributes)
|
96
|
-
end
|
97
|
-
elsif model_class.respond_to?(:create!) # Plain ActiveRecord
|
98
|
-
model = model_class.new
|
99
|
-
model.send(:attributes=, attributes, false) # ignore attr_accessible
|
100
|
-
model.save!
|
101
|
-
model
|
102
|
-
else
|
103
|
-
model_class.new(attributes)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
108
|
-
end
|