Updated Rails to 2.1.0
[gitorious:georgyos-clone.git] / vendor / plugins / rspec_on_rails / lib / spec / rails / example / controller_example_group.rb
1 module Spec
2   module Rails
3     module Example
4       # Controller Examples live in $RAILS_ROOT/spec/controllers/.
5       #
6       # Controller Examples use Spec::Rails::Example::ControllerExampleGroup, which supports running specs for
7       # Controllers in two modes, which represent the tension between the more granular
8       # testing common in TDD and the more high level testing built into
9       # rails. BDD sits somewhere in between: we want to a balance between
10       # specs that are close enough to the code to enable quick fault
11       # isolation and far enough away from the code to enable refactoring
12       # with minimal changes to the existing specs.
13       #
14       # == Isolation mode (default)
15       #
16       # No dependencies on views because none are ever rendered. The
17       # benefit of this mode is that can spec the controller completely
18       # independent of the view, allowing that responsibility to be
19       # handled later, or by somebody else. Combined w/ separate view
20       # specs, this also provides better fault isolation.
21       #
22       # == Integration mode
23       #
24       # To run in this mode, include the +integrate_views+ declaration
25       # in your controller context:
26       #
27       #   describe ThingController do
28       #     integrate_views
29       #     ...
30       #
31       # In this mode, controller specs are run in the same way that
32       # rails functional tests run - one set of tests for both the
33       # controllers and the views. The benefit of this approach is that
34       # you get wider coverage from each spec. Experienced rails
35       # developers may find this an easier approach to begin with, however
36       # we encourage you to explore using the isolation mode and revel
37       # in its benefits.
38       #
39       # == Expecting Errors
40       #
41       # Rspec on Rails will raise errors that occur in controller actions.
42       # In contrast, Rails will swallow errors that are raised in controller
43       # actions and return an error code in the header. If you wish to override
44       # Rspec and have Rail's default behaviour,tell the controller to use
45       # rails error handling ...
46       #
47       #   before(:each) do
48       #     controller.use_rails_error_handling!
49       #   end
50       #
51       # When using Rail's error handling, you can expect error codes in headers ...
52       #
53       #   it "should return an error in the header" do
54       #     response.should be_error
55       #   end
56       #
57       #   it "should return a 501" do
58       #     response.response_code.should == 501
59       #   end
60       #
61       #   it "should return a 501" do
62       #     response.code.should == "501"
63       #   end
64       class ControllerExampleGroup < FunctionalExampleGroup
65         class << self
66                     
67           # Use this to instruct RSpec to render views in your controller examples (Integration Mode).
68           #
69           #   describe ThingController do
70           #     integrate_views
71           #     ...
72           #
73           # See Spec::Rails::Example::ControllerExampleGroup for more information about
74           # Integration and Isolation modes.
75           def integrate_views
76             @integrate_views = true
77           end
78           def integrate_views? # :nodoc:
79             @integrate_views
80           end
81
82           # You MUST provide a controller_name within the context of
83           # your controller specs:
84           #
85           #   describe "ThingController" do
86           #     controller_name :thing
87           #     ...
88           def controller_name(name)
89             @controller_class_name = "#{name}_controller".camelize
90           end
91           attr_accessor :controller_class_name # :nodoc:
92         end
93
94         before(:each) do
95           # Some Rails apps explicitly disable ActionMailer in environment.rb
96           if defined?(ActionMailer)
97             @deliveries = []
98             ActionMailer::Base.deliveries = @deliveries
99           end
100
101           unless @controller.class.ancestors.include?(ActionController::Base)
102             Spec::Expectations.fail_with <<-EOE
103             You have to declare the controller name in controller specs. For example:
104             describe "The ExampleController" do
105             controller_name "example" #invokes the ExampleController
106             end
107             EOE
108           end
109           (class << @controller; self; end).class_eval do
110             def controller_path #:nodoc:
111               self.class.name.underscore.gsub('_controller', '')
112             end
113             include ControllerInstanceMethods
114           end
115           @controller.integrate_views! if @integrate_views
116           @controller.session = session
117         end
118
119         attr_reader :response, :request, :controller
120
121         def initialize(defined_description, &implementation) #:nodoc:
122           super
123           controller_class_name = self.class.controller_class_name
124           if controller_class_name
125             @controller_class_name = controller_class_name.to_s
126           else
127             @controller_class_name = self.class.described_type.to_s
128           end
129           @integrate_views = self.class.integrate_views?
130         end
131
132         # Uses ActionController::Routing::Routes to generate
133         # the correct route for a given set of options.
134         # == Example
135         #   route_for(:controller => 'registrations', :action => 'edit', :id => 1)
136         #     => '/registrations/1;edit'
137         def route_for(options)
138           ensure_that_routes_are_loaded
139           ActionController::Routing::Routes.generate(options)
140         end
141
142         # Uses ActionController::Routing::Routes to parse
143         # an incoming path so the parameters it generates can be checked
144         # == Example
145         #   params_from(:get, '/registrations/1;edit')
146         #     => :controller => 'registrations', :action => 'edit', :id => 1
147         def params_from(method, path)
148           ensure_that_routes_are_loaded
149           ActionController::Routing::Routes.recognize_path(path, :method => method)
150         end
151
152         protected
153         def _assigns_hash_proxy
154           @_assigns_hash_proxy ||= AssignsHashProxy.new @controller
155         end
156
157         private
158         def ensure_that_routes_are_loaded
159           ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
160         end
161
162         module ControllerInstanceMethods #:nodoc:
163           include Spec::Rails::Example::RenderObserver
164
165           # === render(options = nil, deprecated_status_or_extra_options = nil, &block)
166           #
167           # This gets added to the controller's singleton meta class,
168           # allowing Controller Examples to run in two modes, freely switching
169           # from context to context.
170           def render(options=nil, deprecated_status_or_extra_options=nil, &block)
171             if ::Rails::VERSION::STRING >= '2.0.0' && deprecated_status_or_extra_options.nil?
172               deprecated_status_or_extra_options = {}
173             end
174               
175             unless block_given?
176               unless integrate_views?
177                 if @template.respond_to?(:finder)
178                   (class << @template.finder; self; end).class_eval do
179                     define_method :file_exists? do
180                       true
181                     end
182                   end
183                 else
184                   (class << @template; self; end).class_eval do
185                     define_method :file_exists? do
186                       true
187                     end
188                   end
189                 end
190                 (class << @template; self; end).class_eval do
191                   define_method :render_file do |*args|
192                     @first_render ||= args[0]
193                   end
194                 end
195               end
196             end
197
198             if matching_message_expectation_exists(options)
199               expect_render_mock_proxy.render(options, &block)
200               @performed_render = true
201             else
202               unless matching_stub_exists(options)
203                 super(options, deprecated_status_or_extra_options, &block)
204               end
205             end
206           end
207           
208           private
209             def matching_message_expectation_exists(options)
210               expect_render_mock_proxy.send(:__mock_proxy).send(:find_matching_expectation, :render, options)
211             end
212           
213             def matching_stub_exists(options)
214               expect_render_mock_proxy.send(:__mock_proxy).send(:find_matching_method_stub, :render, options)
215             end
216           
217           public
218           if self.respond_to?(:should_receive) && self.respond_to?(:stub!)
219             self.send :alias_method, :orig_should_receive, :should_receive
220             self.send :alias_method, :orig_stub!, :stub!
221             def raise_with_disable_message(old_method, new_method)
222               raise %Q|
223         controller.#{old_method}(:render) has been disabled because it
224         can often produce unexpected results. Instead, you should
225         use the following (before the action):
226
227         controller.#{new_method}(*args)
228
229         See the rdoc for #{new_method} for more information.
230               |
231             end
232             def should_receive(*args)
233               if args[0] == :render
234                 raise_with_disable_message("should_receive", "expect_render")
235               else
236                 orig_should_receive(*args)
237               end
238             end
239             def stub!(*args)
240               if args[0] == :render
241                 raise_with_disable_message("stub!", "stub_render")
242               else
243                 orig_stub!(*args)
244               end
245             end
246           end
247
248           def response(&block)
249             # NOTE - we're setting @update for the assert_select_spec - kinda weird, huh?
250             @update = block
251             @_response || @response
252           end
253
254           def integrate_views!
255             @integrate_views = true
256           end
257
258           private
259
260           def integrate_views?
261             @integrate_views
262           end
263         end
264
265         Spec::Example::ExampleGroupFactory.register(:controller, self)
266       end
267     end
268   end
269 end