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