rspec-rails 2.8.0.rc1 → 2.8.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/License.txt +22 -0
  2. data/README.md +253 -119
  3. data/features/README.md +0 -2
  4. data/features/Upgrade.md +1 -1
  5. data/lib/generators/rspec/install/templates/spec/spec_helper.rb +1 -1
  6. data/lib/generators/rspec/integration/integration_generator.rb +1 -0
  7. data/lib/generators/rspec/scaffold/scaffold_generator.rb +1 -0
  8. data/lib/generators/rspec/scaffold/templates/controller_spec.rb +23 -16
  9. data/lib/rspec/rails/adapters.rb +25 -13
  10. data/lib/rspec/rails/example/controller_example_group.rb +65 -122
  11. data/lib/rspec/rails/example/helper_example_group.rb +11 -36
  12. data/lib/rspec/rails/example/mailer_example_group.rb +1 -1
  13. data/lib/rspec/rails/example/rails_example_group.rb +5 -0
  14. data/lib/rspec/rails/example/request_example_group.rb +4 -19
  15. data/lib/rspec/rails/example/routing_example_group.rb +8 -10
  16. data/lib/rspec/rails/example/view_example_group.rb +21 -35
  17. data/lib/rspec/rails/extensions/active_record/base.rb +15 -12
  18. data/lib/rspec/rails/fixture_support.rb +1 -1
  19. data/lib/rspec/rails/matchers/be_a_new.rb +63 -30
  20. data/lib/rspec/rails/matchers/be_new_record.rb +18 -3
  21. data/lib/rspec/rails/matchers/have_extension.rb +26 -14
  22. data/lib/rspec/rails/matchers/redirect_to.rb +26 -7
  23. data/lib/rspec/rails/matchers/relation_match_array.rb +1 -1
  24. data/lib/rspec/rails/matchers/render_template.rb +27 -8
  25. data/lib/rspec/rails/matchers/routing_matchers.rb +72 -24
  26. data/lib/rspec/rails/mocks.rb +42 -34
  27. data/lib/rspec/rails/module_inclusion.rb +2 -1
  28. data/lib/rspec/rails/version.rb +1 -1
  29. data/lib/rspec/rails/view_assigns.rb +31 -34
  30. data/lib/rspec/rails/view_rendering.rb +10 -6
  31. data/spec/rspec/rails/example/controller_example_group_spec.rb +2 -2
  32. data/spec/rspec/rails/example/helper_example_group_spec.rb +5 -5
  33. data/spec/rspec/rails/example/view_example_group_spec.rb +5 -5
  34. data/spec/rspec/rails/matchers/be_a_new_spec.rb +2 -0
  35. data/spec/rspec/rails/matchers/be_new_record_spec.rb +2 -0
  36. data/spec/rspec/rails/matchers/render_template_spec.rb +3 -5
  37. data/spec/rspec/rails/mocks/mock_model_spec.rb +28 -0
  38. metadata +22 -19
@@ -45,8 +45,6 @@ your preference to the Gemfile:
45
45
  gem "webrat"
46
46
  gem "capybara"
47
47
 
48
- Note that Capybara matchers are not available in view or helper specs.
49
-
50
48
  ## Issues
51
49
 
52
50
  The documentation for rspec-rails is a work in progress. We'll be adding
@@ -36,7 +36,7 @@ is what you need to change:
36
36
 
37
37
  ## Controller specs
38
38
 
39
- ### islation from view templates
39
+ ### isolation from view templates
40
40
 
41
41
  By default, controller specs do _not_ render view templates. This keeps
42
42
  controller specs isolated from the content of views and their requirements.
@@ -9,7 +9,7 @@ require 'rspec/autorun'
9
9
  Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
10
10
 
11
11
  RSpec.configure do |config|
12
- # == Mock Framework
12
+ # ## Mock Framework
13
13
  #
14
14
  # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
15
15
  #
@@ -16,6 +16,7 @@ module Rspec
16
16
 
17
17
  protected
18
18
 
19
+ # @deprecated Use `--webrat` instead.
19
20
  def webrat?
20
21
  RSpec.deprecate("the --webrat-matchers option", "--webrat") if options[:webrat_matchers]
21
22
  options[:webrat] || options[:webrat_matchers]
@@ -51,6 +51,7 @@ module Rspec
51
51
 
52
52
  protected
53
53
 
54
+ # @deprecated Use `--webrat` instead.
54
55
  def webrat?
55
56
  RSpec.deprecate("--webrat-matchers", "--webrat") if options[:webrat_matchers]
56
57
  options[:webrat] || options[:webrat_matchers]
@@ -26,12 +26,19 @@ describe <%= controller_class_name %>Controller do
26
26
  def valid_attributes
27
27
  {}
28
28
  end
29
+
30
+ # This should return the minimal set of values that should be in the session
31
+ # in order to pass any filters (e.g. authentication) defined in
32
+ # <%= controller_class_name %>Controller. Be sure to keep this updated too.
33
+ def valid_session
34
+ {}
35
+ end
29
36
 
30
37
  <% unless options[:singleton] -%>
31
38
  describe "GET index" do
32
39
  it "assigns all <%= table_name.pluralize %> as @<%= table_name.pluralize %>" do
33
40
  <%= file_name %> = <%= class_name %>.create! valid_attributes
34
- get :index
41
+ get :index, {}, valid_session
35
42
  assigns(:<%= table_name %>).should eq([<%= file_name %>])
36
43
  end
37
44
  end
@@ -40,14 +47,14 @@ describe <%= controller_class_name %>Controller do
40
47
  describe "GET show" do
41
48
  it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
42
49
  <%= file_name %> = <%= class_name %>.create! valid_attributes
43
- get :show, :id => <%= file_name %>.id
50
+ get :show, {:id => <%= file_name %>.to_param}, valid_session
44
51
  assigns(:<%= ns_file_name %>).should eq(<%= file_name %>)
45
52
  end
46
53
  end
47
54
 
48
55
  describe "GET new" do
49
56
  it "assigns a new <%= ns_file_name %> as @<%= ns_file_name %>" do
50
- get :new
57
+ get :new, {}, valid_session
51
58
  assigns(:<%= ns_file_name %>).should be_a_new(<%= class_name %>)
52
59
  end
53
60
  end
@@ -55,7 +62,7 @@ describe <%= controller_class_name %>Controller do
55
62
  describe "GET edit" do
56
63
  it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
57
64
  <%= file_name %> = <%= class_name %>.create! valid_attributes
58
- get :edit, :id => <%= file_name %>.id
65
+ get :edit, {:id => <%= file_name %>.to_param}, valid_session
59
66
  assigns(:<%= ns_file_name %>).should eq(<%= file_name %>)
60
67
  end
61
68
  end
@@ -64,18 +71,18 @@ describe <%= controller_class_name %>Controller do
64
71
  describe "with valid params" do
65
72
  it "creates a new <%= class_name %>" do
66
73
  expect {
67
- post :create, :<%= ns_file_name %> => valid_attributes
74
+ post :create, {:<%= ns_file_name %> => valid_attributes}, valid_session
68
75
  }.to change(<%= class_name %>, :count).by(1)
69
76
  end
70
77
 
71
78
  it "assigns a newly created <%= ns_file_name %> as @<%= ns_file_name %>" do
72
- post :create, :<%= ns_file_name %> => valid_attributes
79
+ post :create, {:<%= ns_file_name %> => valid_attributes}, valid_session
73
80
  assigns(:<%= ns_file_name %>).should be_a(<%= class_name %>)
74
81
  assigns(:<%= ns_file_name %>).should be_persisted
75
82
  end
76
83
 
77
84
  it "redirects to the created <%= ns_file_name %>" do
78
- post :create, :<%= ns_file_name %> => valid_attributes
85
+ post :create, {:<%= ns_file_name %> => valid_attributes}, valid_session
79
86
  response.should redirect_to(<%= class_name %>.last)
80
87
  end
81
88
  end
@@ -84,14 +91,14 @@ describe <%= controller_class_name %>Controller do
84
91
  it "assigns a newly created but unsaved <%= ns_file_name %> as @<%= ns_file_name %>" do
85
92
  # Trigger the behavior that occurs when invalid params are submitted
86
93
  <%= class_name %>.any_instance.stub(:save).and_return(false)
87
- post :create, :<%= ns_file_name %> => {}
94
+ post :create, {:<%= ns_file_name %> => {}}, valid_session
88
95
  assigns(:<%= ns_file_name %>).should be_a_new(<%= class_name %>)
89
96
  end
90
97
 
91
98
  it "re-renders the 'new' template" do
92
99
  # Trigger the behavior that occurs when invalid params are submitted
93
100
  <%= class_name %>.any_instance.stub(:save).and_return(false)
94
- post :create, :<%= ns_file_name %> => {}
101
+ post :create, {:<%= ns_file_name %> => {}}, valid_session
95
102
  response.should render_template("new")
96
103
  end
97
104
  end
@@ -106,18 +113,18 @@ describe <%= controller_class_name %>Controller do
106
113
  # receives the :update_attributes message with whatever params are
107
114
  # submitted in the request.
108
115
  <%= class_name %>.any_instance.should_receive(:update_attributes).with(<%= params %>)
109
- put :update, :id => <%= file_name %>.id, :<%= ns_file_name %> => <%= params %>
116
+ put :update, {:id => <%= file_name %>.to_param, :<%= ns_file_name %> => <%= params %>}, valid_session
110
117
  end
111
118
 
112
119
  it "assigns the requested <%= ns_file_name %> as @<%= ns_file_name %>" do
113
120
  <%= file_name %> = <%= class_name %>.create! valid_attributes
114
- put :update, :id => <%= file_name %>.id, :<%= ns_file_name %> => valid_attributes
121
+ put :update, {:id => <%= file_name %>.to_param, :<%= ns_file_name %> => valid_attributes}, valid_session
115
122
  assigns(:<%= ns_file_name %>).should eq(<%= file_name %>)
116
123
  end
117
124
 
118
125
  it "redirects to the <%= ns_file_name %>" do
119
126
  <%= file_name %> = <%= class_name %>.create! valid_attributes
120
- put :update, :id => <%= file_name %>.id, :<%= ns_file_name %> => valid_attributes
127
+ put :update, {:id => <%= file_name %>.to_param, :<%= ns_file_name %> => valid_attributes}, valid_session
121
128
  response.should redirect_to(<%= file_name %>)
122
129
  end
123
130
  end
@@ -127,7 +134,7 @@ describe <%= controller_class_name %>Controller do
127
134
  <%= file_name %> = <%= class_name %>.create! valid_attributes
128
135
  # Trigger the behavior that occurs when invalid params are submitted
129
136
  <%= class_name %>.any_instance.stub(:save).and_return(false)
130
- put :update, :id => <%= file_name %>.id, :<%= ns_file_name %> => {}
137
+ put :update, {:id => <%= file_name %>.to_param, :<%= ns_file_name %> => {}}, valid_session
131
138
  assigns(:<%= ns_file_name %>).should eq(<%= file_name %>)
132
139
  end
133
140
 
@@ -135,7 +142,7 @@ describe <%= controller_class_name %>Controller do
135
142
  <%= file_name %> = <%= class_name %>.create! valid_attributes
136
143
  # Trigger the behavior that occurs when invalid params are submitted
137
144
  <%= class_name %>.any_instance.stub(:save).and_return(false)
138
- put :update, :id => <%= file_name %>.id, :<%= ns_file_name %> => {}
145
+ put :update, {:id => <%= file_name %>.to_param, :<%= ns_file_name %> => {}}, valid_session
139
146
  response.should render_template("edit")
140
147
  end
141
148
  end
@@ -145,13 +152,13 @@ describe <%= controller_class_name %>Controller do
145
152
  it "destroys the requested <%= ns_file_name %>" do
146
153
  <%= file_name %> = <%= class_name %>.create! valid_attributes
147
154
  expect {
148
- delete :destroy, :id => <%= file_name %>.id
155
+ delete :destroy, {:id => <%= file_name %>.to_param}, valid_session
149
156
  }.to change(<%= class_name %>, :count).by(-1)
150
157
  end
151
158
 
152
159
  it "redirects to the <%= table_name %> list" do
153
160
  <%= file_name %> = <%= class_name %>.create! valid_attributes
154
- delete :destroy, :id => <%= file_name %>.id
161
+ delete :destroy, {:id => <%= file_name %>.to_param}, valid_session
155
162
  response.should redirect_to(<%= index_helper %>_url)
156
163
  end
157
164
  end
@@ -7,50 +7,62 @@ module RSpec
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  module ClassMethods
10
+ # @api private
11
+ #
12
+ # Wraps `setup` calls from within Rails' testing framework in `before`
13
+ # hooks.
10
14
  def setup(*methods)
11
15
  methods.each {|method| before { send method } }
12
16
  end
13
17
 
18
+ # @api private
19
+ #
20
+ # Wraps `teardown` calls from within Rails' testing framework in
21
+ # `after` hooks.
14
22
  def teardown(*methods)
15
23
  methods.each {|method| after { send method } }
16
24
  end
17
25
  end
18
26
 
19
- module InstanceMethods
20
- def method_name
21
- @example
22
- end
27
+ # @api private
28
+ def method_name
29
+ @example
23
30
  end
24
31
  end
25
32
 
26
33
  module TestUnitAssertionAdapter
27
34
  extend ActiveSupport::Concern
28
35
 
29
- class AssertionDelegate
30
- include Test::Unit::Assertions
31
- end
32
-
33
36
  module ClassMethods
37
+ # @api private
38
+ #
39
+ # Returns the names of assertion methods that we want to expose to
40
+ # examples without exposing non-assertion methods in Test::Unit or
41
+ # Minitest.
34
42
  def assertion_method_names
35
43
  Test::Unit::Assertions.public_instance_methods.select{|m| m.to_s =~ /^(assert|flunk)/} +
36
44
  [:build_message]
37
45
  end
38
46
 
47
+ # @api private
39
48
  def define_assertion_delegators
40
49
  assertion_method_names.each do |m|
41
50
  class_eval <<-CODE
42
51
  def #{m}(*args, &block)
43
- assertion_delegate.send :#{m}, *args, &block
52
+ assertion_delegator.send :#{m}, *args, &block
44
53
  end
45
54
  CODE
46
55
  end
47
56
  end
48
57
  end
49
58
 
50
- module InstanceMethods
51
- def assertion_delegate
52
- @assertion_delegate ||= AssertionDelegate.new
53
- end
59
+ class AssertionDelegator
60
+ include Test::Unit::Assertions
61
+ end
62
+
63
+ # @api private
64
+ def assertion_delegator
65
+ @assertion_delegator ||= AssertionDelegator.new
54
66
  end
55
67
 
56
68
  included do
@@ -3,81 +3,6 @@ RSpec.configure do |config|
3
3
  end
4
4
 
5
5
  module RSpec::Rails
6
- # Extends ActionController::TestCase::Behavior to work with RSpec.
7
- #
8
- # == Examples
9
- #
10
- # == with stubs
11
- #
12
- # describe WidgetsController do
13
- # describe "GET index" do
14
- # it "assigns all widgets to @widgets" do
15
- # widget = stub_model(Widget)
16
- # Widget.stub(:all) { widget }
17
- # get :index
18
- # assigns(:widgets).should eq([widget])
19
- # end
20
- # end
21
- # end
22
- #
23
- # === with a factory
24
- #
25
- # describe WidgetsController do
26
- # describe "GET index" do
27
- # it "assigns all widgets to @widgets" do
28
- # widget = Factory(:widget)
29
- # get :index
30
- # assigns(:widgets).should eq([widget])
31
- # end
32
- # end
33
- # end
34
- #
35
- # === with fixtures
36
- #
37
- # describe WidgetsController do
38
- # describe "GET index" do
39
- # fixtures :widgets
40
- #
41
- # it "assigns all widgets to @widgets" do
42
- # get :index
43
- # assigns(:widgets).should eq(Widget.all)
44
- # end
45
- # end
46
- # end
47
- #
48
- # == Matchers
49
- #
50
- # In addition to the stock matchers from rspec-expectations, controller
51
- # specs add these matchers, which delegate to rails' assertions:
52
- #
53
- # response.should render_template(*args)
54
- # => delegates to assert_template(*args)
55
- #
56
- # response.should redirect_to(destination)
57
- # => delegates to assert_redirected_to(destination)
58
- #
59
- # == Isolation from views
60
- #
61
- # RSpec's preferred approach to spec'ing controller behaviour is to isolate
62
- # the controller from its collaborators. By default, therefore, controller
63
- # example groups do not render the views in your app. Due to the way Rails
64
- # searches for view templates, the template still needs to exist, but it
65
- # won't actually be loaded.
66
- #
67
- # NOTE that this is different from rspec-rails-1 with rails-2, which did not
68
- # require the presence of the file at all. Due to changes in rails-3, this
69
- # was no longer feasible in rspec-rails-2.
70
- #
71
- # == View rendering
72
- #
73
- # If you prefer a more integrated approach, similar to that of Rails'
74
- # functional tests, you can tell controller groups to render the views in the
75
- # app with the +render_views+ declaration:
76
- #
77
- # describe WidgetsController do
78
- # render_views
79
- # ...
80
- #
81
6
  module ControllerExampleGroup
82
7
  extend ActiveSupport::Concern
83
8
  include RSpec::Rails::RailsExampleGroup
@@ -88,53 +13,53 @@ module RSpec::Rails
88
13
  include RSpec::Rails::Matchers::RoutingMatchers
89
14
 
90
15
  module ClassMethods
16
+ # @private
91
17
  def controller_class
92
- describes
18
+ described_class
93
19
  end
94
20
 
95
- # Supports a simple DSL for specifying behaviour of
96
- # ApplicationController. Creates an anonymous subclass of
97
- # ApplicationController and evals the +body+ in that context. Also sets
98
- # up implicit routes for this controller, that are separate from those
99
- # defined in <tt>config/routes.rb</tt>.
21
+ # Supports a simple DSL for specifying behavior of ApplicationController.
22
+ # Creates an anonymous subclass of ApplicationController and evals the
23
+ # `body` in that context. Also sets up implicit routes for this
24
+ # controller, that are separate from those defined in "config/routes.rb".
100
25
  #
101
- # == Examples
26
+ # @note Due to Ruby 1.8 scoping rules in anoymous subclasses, constants
27
+ # defined in `ApplicationController` must be fully qualified (e.g.
28
+ # `ApplicationController::AccessDenied`) in the block passed to the
29
+ # `controller` method. Any instance methods, filters, etc, that are
30
+ # defined in `ApplicationController`, however, are accessible from
31
+ # within the block.
102
32
  #
103
- # describe ApplicationController do
104
- # controller do
105
- # def index
106
- # raise ApplicationController::AccessDenied
107
- # end
108
- # end
33
+ # @example
109
34
  #
110
- # describe "handling AccessDenied exceptions" do
111
- # it "redirects to the /401.html page" do
112
- # get :index
113
- # response.should redirect_to("/401.html")
114
- # end
115
- # end
116
- # end
35
+ # describe ApplicationController do
36
+ # controller do
37
+ # def index
38
+ # raise ApplicationController::AccessDenied
39
+ # end
40
+ # end
41
+ #
42
+ # describe "handling AccessDenied exceptions" do
43
+ # it "redirects to the /401.html page" do
44
+ # get :index
45
+ # response.should redirect_to("/401.html")
46
+ # end
47
+ # end
48
+ # end
117
49
  #
118
50
  # If you would like to spec a subclass of ApplicationController, call
119
51
  # controller like so:
120
52
  #
121
- # controller(ApplicationControllerSubclass) do
122
- # # ....
123
- # end
124
- #
125
- # NOTICE: Due to Ruby 1.8 scoping rules in anoymous subclasses, constants
126
- # defined in +ApplicationController+ must be fully qualified (e.g.
127
- # ApplicationController::AccessDenied) in the block passed to the
128
- # +controller+ method. Any instance methods, filters, etc, that are
129
- # defined in +ApplicationController+, however, are accessible from within
130
- # the block.
53
+ # controller(ApplicationControllerSubclass) do
54
+ # # ....
55
+ # end
131
56
  def controller(base_class = nil, &body)
132
57
  base_class ||= RSpec.configuration.infer_base_class_for_anonymous_controllers? ?
133
58
  controller_class :
134
59
  ApplicationController
135
60
 
136
- metadata[:example_group][:describes] = Class.new(base_class, &body)
137
- metadata[:example_group][:describes].singleton_class.class_eval do
61
+ metadata[:example_group][:described_class] = Class.new(base_class, &body)
62
+ metadata[:example_group][:described_class].singleton_class.class_eval do
138
63
  def name; "AnonymousController" end
139
64
  end
140
65
 
@@ -149,25 +74,43 @@ module RSpec::Rails
149
74
  end
150
75
  end
151
76
 
152
- module InstanceMethods
153
- attr_reader :controller, :routes
77
+ attr_reader :controller, :routes
154
78
 
155
- module BypassRescue
156
- def rescue_with_handler(exception)
157
- raise exception
158
- end
79
+ module BypassRescue
80
+ def rescue_with_handler(exception)
81
+ raise exception
159
82
  end
83
+ end
160
84
 
161
- def bypass_rescue
162
- controller.extend(BypassRescue)
163
- end
85
+ # Extends the controller with a module that overrides
86
+ # `rescue_with_handler` to raise the exception passed to it. Use this to
87
+ # specify that an action _should_ raise an exception given appropriate
88
+ # conditions.
89
+ #
90
+ # @example
91
+ #
92
+ # describe ProfilesController do
93
+ # it "raises a 403 when a non-admin user tries to view another user's profile" do
94
+ # profile = create_profile
95
+ # login_as profile.user
96
+ #
97
+ # expect do
98
+ # bypass_rescue
99
+ # get :show, :id => profile.id + 1
100
+ # end.to raise_error(/403 Forbidden/)
101
+ # end
102
+ # end
103
+ def bypass_rescue
104
+ controller.extend(BypassRescue)
105
+ end
164
106
 
165
- def method_missing(method, *args, &block)
166
- if @orig_routes && @orig_routes.named_routes.helpers.include?(method)
167
- controller.send(method, *args, &block)
168
- else
169
- super
170
- end
107
+ # If method is a named_route, delegates to the RouteSet associated with
108
+ # this controller.
109
+ def method_missing(method, *args, &block)
110
+ if @orig_routes && @orig_routes.named_routes.helpers.include?(method)
111
+ controller.send(method, *args, &block)
112
+ else
113
+ super
171
114
  end
172
115
  end
173
116