rspec-rails 2.8.0.rc1 → 2.8.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
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