changelog shortlog tags changeset manifest revisions annotate raw

vendor/plugins/rspec_on_rails/lib/spec/rails/example/controller_example_group.rb

changeset 16: 01fd3f10ae84
author: moriq@moriq.com
date: Mon Mar 10 10:13:18 2008 +0900 (16 years ago)
permissions: -rw-r--r--
description: add plugins rspec_on_rails
1module 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 @controller.metaclass.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 _controller_ivar_proxy
154 @controller_ivar_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 = 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=nil, &block)
171 unless block_given?
172 unless integrate_views?
173 @template.metaclass.class_eval do
174 define_method :file_exists? do
175 true
176 end
177 define_method :render_file do |*args|
178 @first_render ||= args[0]
179 end
180 end
181 end
182 end
183
184 if matching_message_expectation_exists(options)
185 expect_render_mock_proxy.render(options, &block)
186 @performed_render = true
187 else
188 unless matching_stub_exists(options)
189 super(options, deprecated_status, &block)
190 end
191 end
192 end
193
194 private
195 def matching_message_expectation_exists(options)
196 expect_render_mock_proxy.send(:__mock_proxy).send(:find_matching_expectation, :render, options)
197 end
198
199 def matching_stub_exists(options)
200 expect_render_mock_proxy.send(:__mock_proxy).send(:find_matching_method_stub, :render, options)
201 end
202
203 public
204 if self.respond_to?(:should_receive) && self.respond_to?(:stub!)
205 self.send :alias_method, :orig_should_receive, :should_receive
206 self.send :alias_method, :orig_stub!, :stub!
207 def raise_with_disable_message(old_method, new_method)
208 raise %Q|
209 controller.#{old_method}(:render) has been disabled because it
210 can often produce unexpected results. Instead, you should
211 use the following (before the action):
212
213 controller.#{new_method}(*args)
214
215 See the rdoc for #{new_method} for more information.
216 |
217 end
218 def should_receive(*args)
219 if args[0] == :render
220 raise_with_disable_message("should_receive", "expect_render")
221 else
222 orig_should_receive(*args)
223 end
224 end
225 def stub!(*args)
226 if args[0] == :render
227 raise_with_disable_message("stub!", "stub_render")
228 else
229 orig_stub!(*args)
230 end
231 end
232 end
233
234 def response(&block)
235 # NOTE - we're setting @update for the assert_select_spec - kinda weird, huh?
236 @update = block
237 @_response || @response
238 end
239
240 def integrate_views!
241 @integrate_views = true
242 end
243
244 private
245
246 def integrate_views?
247 @integrate_views
248 end
249 end
250
251 Spec::Example::ExampleGroupFactory.register(:controller, self)
252 end
253 end
254 end
255end