-
Notifications
You must be signed in to change notification settings - Fork 2
/
README
266 lines (201 loc) · 10 KB
/
README
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
= Deprecated -- Use Dataset
Scenarios has been replaced by Dataset, a complete rewrite, test driven. It has a number of bugs fixed and more features. Check out http://github.com/aiwilliams/dataset. I hope you find it far more useful and worth helping to extend.
== Rails Scenarios Plugin
Who hasn't experienced the pain of dozens of YAML files filled with hundreds
of inter-related data structures? When do you look at People of an
Organization and not have to look at the organization_id, open the
organizations.yml file, and search for 'id: X'?
In a nutshell, scenarios are a drop in replacement for YAML fixtures. Instead
of encouraging you to create a mindless amount of raw data in the form of
YAML, scenarios encourage you to create code that populates your tables with
the appropriate records.
How is it different from other solutions? A few things:
* It continues to provide a fundamental, fast insertion method using attributes
written directly to a table. This is the
Scenarios::TableMethods#create_record method.
* It allows you to create records using validations if you prefer, or if it's
important to have all your callbacks be invoked. See
Scenarios::TableMethods#create_model. Both create_record and create_model
allow you to name your instances for retrieval by the instance and id reader
methods (more below).
* Nothing stops you from simply invoking YouModel.create!, etc. We'll still
keep track of the tables the scenario modifies and clean things up afterward.
* It allows you to create re-usable scenarios as classes. These classes are
like any other class - they may include modules, subclass, and be composed of
other scenarios. See Scenarios::Base.uses. This also means that you can load
any scenario into any Rails environment. That's what the 'rake
db:scenario:load' task is good for (more below). Very handy for re-using all
that test support code to create populated demos!
=== Quick Start
Since Scenarios is a Rails plugin at this time, you should get it installed,
using the appropriate method (script/plugin, svn, piston) into your
vendor/plugins directory. Once you have this, in your spec_helper.rb or
test_helper.rb, add the following line after the spec requires:
require 'scenarios'
The Scenarios you write should be placed in the spec/scenarios directory of your
Rails project if you're using RSpec, or the test/scenarios directory of your
Rails project if you're using Test::Unit. Scenario file names always end in
"_scenario.rb" and classes end in "Scenario".
A simple scenario looks like this:
# in spec/scenarios/users_scenario.rb or test/scenarios/users_scenario.rb
class UsersScenario < Scenario::Base
def load
create_record :user, :john, :name => 'John', :password => 'doodaht'
create_record :user, :cindy, :name => 'Cindy', :password => 'whoot!'
end
end
In the example above, I'm using the <tt>create_record</tt> instance method to
create two users: John and Cindy. Notice the calls to <tt>create_record</tt>.
There are three parameters. The first is the singular name of the table to
insert the record into, the second is the symbolic name of the record (more on
that later), and the third is a hash of the attributes of the record.
To use the UsersScenario in a description, you should declare it using
the <tt>scenario</tt> method. Here it is within a spec file (RSpec):
# in spec/models/user_spec.rb
describe User do
scenario :users
it "should allow me to do something with John" do
user = users(:john)
user.password.should == "doodaht"
end
end
and here it is within a standard Test::Unit test:
# in test/unit/user_test.rb
class UserTest < Test::Unit::TestCase
scenario :users
def test_do_something
user = users(:john)
assert_equal "doodaht", user.password
end
end
Notice that it is easy to load an instance of a model object using its
symbolic name with a reader method, similar to that of Rails' fixtures. In the
example above, I loaded John with the reader method <tt>users</tt> and the
symbolic name <tt>:john</tt>. (Remember that in the Users scenario I declared
that John should be accessible through the symbolic name <tt>:john</tt>.)
I could also have retrieved an array of user fixtures by passing in
multiple symbolic names to the reader method:
# in spec/models/user_spec.rb
describe User do
scenario :users
it "should allow me to get all admins" do
admins = users(:john, :ryan)
User.admins.should eql(admins)
end
end
=== Composition
In real life your scenarios will probably grow quite complicated. The
scenarios plugin allows you to deal with this complexity through composition.
Here's a simple example:
# in spec/scenarios/posts_scenario.rb or test/scenarios/posts_scenario.rb
class PostsScenario < Scenario::Base
def load
create_record :post, :first, :title => "First Post"
create_record :post, :second, :title => "Second Post"
end
end
# in spec/scenarios/comments_scenario.rb or test/scenarios/comments_scenario.rb
class CommentsScenario < Scenario::Base
uses :posts
def load
create_record :comment, :first, :body => "Nice post!", :post_id => post_id(:first)
create_record :comment, :second, :body => "I like it.", :post_id => post_id(:first)
create_record :comment, :third, :body => "I thoroughly disagree.", :post_id => post_id(:second)
end
end
In the example above, the CommentsScenario declares that it depends on the
Posts scenario with the <tt>uses</tt> class method. This means that if you
load the CommentsScenario, the PostsScenario will be loaded first and the
CommentsScenario will have access to all the data loaded by the PostsScenario
in its own <tt>load</tt> method. Note that inside the load method I'm using
another form of reader methed which simply gives you the id for a symbolic
name (in this case: <tt>post_id</tt>). This is most useful for making
associations, as done here with comments and posts.
=== Helper Methods
Another way of simplifying your scenarios and specs/tests is through helper
methods. The Scenarios plugin provides a handy way to declare helper methods
that are accessible from inside the scenario and also from inside related
RSpec/Test::Unit examples:
# in spec/scenarios/users_scenario.rb or test/scenarios/users_scenario.rb
class UsersScenario < Scenario::Base
def load
create_user :name => "John"
end
helpers do
def create_user(attributes={})
create_record :user, attributes[:name].downcase.intern, attributes
end
def login_as(user)
@request.session[:user_id] = user.id
end
end
end
Helper methods declared inside the helpers block are mixed into the scenario
when it is instantiated and mixed into examples that declare that they are using
the scenario. Also, in the case where one scenario <tt>uses</tt> another, the
using scenario will have the helper methods of the used scenario.
# in spec/controllers/projects_controller_spec.rb
describe "Projects screen" do
scenario :users
it "should show active projects" do
login_as(users(:john))
get :projects
@response.should have_tag('#active_projects')
end
end
# in test/functional/projects_controller_test.rb
class PeopleControllerTest < Test::Unit::TestCase
scenario :users
def test_index
login_as(users(:john))
get :projects
assert_tag('#active_projects')
end
end
Notice that within my specs/tests I have access to the login_as helper method
declared inside the <tt>helpers</tt> block of the UsersScenario. Scenario
helpers are a great way to share helper methods between specs/tests that use a
specific scenario.
=== Built-in Scenario
There is a scenario named 'blank' that comes with the plugin. This scenario is
useful when you want to express, and guarantee, that the database is empty. It
works by using your db/schema.rb, so if the table isn't created in there, it
won't be cleaned up.
Scenario.load_paths is an array of the locations to look for scenario
definitions. The built-in scenarios directory is consulted last, so if you'd
like to re-define, for instance, the 'blank' scenario, simply create
'blank_scenario.rb' in your spec/scenarios or test/scenarios directory.
=== Load Rake Task
The Scenarios plugin provides a single Rake task, <tt>db:scenario:load</tt>,
which you may use in a fashion similar to Rails fixtures'
<tt>db:fixtures:load</tt>.
rake db:scenario:load SCENARIO=comments
When invoked, this task will populate the development database with the named
scenario.
If you do not specify SCENARIO, the task will expect to find a default scenario
(a file 'default_scenario.rb' having DefaultScenario defined in it). It is our
practice to have it such that this scenario <tt>uses</tt> a number of our other
scenarios, thereby:
* encouraging us to use test data that looks good in the running development
application
* allowing us to troubleshoot failing tests in the running development
application
=== More Information
For more information, be sure to look through the documentation over at RubyForge:
* http://faithfulcode.rubyforge.org/docs/scenarios
You might also enjoy taking a look at the specs for the plugin and the example
scenarios:
* http://faithfulcode.rubyforge.org/svn/plugins/trunk/scenarios/spec/scenarios_spec.rb
* http://faithfulcode.rubyforge.org/svn/plugins/trunk/scenarios/spec/scenarios
Browse the complete source code:
* http://faithfulcode.rubyforge.org/svn/plugins/trunk/scenarios
=== Running Tests
You should be able to simply run rake. Notice in testing/environment.rb the
revisions under which this project will work. If you intend to test against
HEAD, you will need to delete the directory testing/tmp/trunk/HEAD. At some
point, it would be nice to have the script track the revision of HEAD that we
have, and update the directory automatically.
=== License
The Scenarios plugin is released under the MIT-License and is Copyright (c)
2007, Adam Williams and John W. Long. Special thanks to Chris Redinger for his
part in helping us get this plugin ready for the public.