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.
- data/License.txt +22 -0
- data/README.md +253 -119
- data/features/README.md +0 -2
- data/features/Upgrade.md +1 -1
- data/lib/generators/rspec/install/templates/spec/spec_helper.rb +1 -1
- data/lib/generators/rspec/integration/integration_generator.rb +1 -0
- data/lib/generators/rspec/scaffold/scaffold_generator.rb +1 -0
- data/lib/generators/rspec/scaffold/templates/controller_spec.rb +23 -16
- data/lib/rspec/rails/adapters.rb +25 -13
- data/lib/rspec/rails/example/controller_example_group.rb +65 -122
- data/lib/rspec/rails/example/helper_example_group.rb +11 -36
- data/lib/rspec/rails/example/mailer_example_group.rb +1 -1
- data/lib/rspec/rails/example/rails_example_group.rb +5 -0
- data/lib/rspec/rails/example/request_example_group.rb +4 -19
- data/lib/rspec/rails/example/routing_example_group.rb +8 -10
- data/lib/rspec/rails/example/view_example_group.rb +21 -35
- data/lib/rspec/rails/extensions/active_record/base.rb +15 -12
- data/lib/rspec/rails/fixture_support.rb +1 -1
- data/lib/rspec/rails/matchers/be_a_new.rb +63 -30
- data/lib/rspec/rails/matchers/be_new_record.rb +18 -3
- data/lib/rspec/rails/matchers/have_extension.rb +26 -14
- data/lib/rspec/rails/matchers/redirect_to.rb +26 -7
- data/lib/rspec/rails/matchers/relation_match_array.rb +1 -1
- data/lib/rspec/rails/matchers/render_template.rb +27 -8
- data/lib/rspec/rails/matchers/routing_matchers.rb +72 -24
- data/lib/rspec/rails/mocks.rb +42 -34
- data/lib/rspec/rails/module_inclusion.rb +2 -1
- data/lib/rspec/rails/version.rb +1 -1
- data/lib/rspec/rails/view_assigns.rb +31 -34
- data/lib/rspec/rails/view_rendering.rb +10 -6
- data/spec/rspec/rails/example/controller_example_group_spec.rb +2 -2
- data/spec/rspec/rails/example/helper_example_group_spec.rb +5 -5
- data/spec/rspec/rails/example/view_example_group_spec.rb +5 -5
- data/spec/rspec/rails/matchers/be_a_new_spec.rb +2 -0
- data/spec/rspec/rails/matchers/be_new_record_spec.rb +2 -0
- data/spec/rspec/rails/matchers/render_template_spec.rb +3 -5
- data/spec/rspec/rails/mocks/mock_model_spec.rb +28 -0
- metadata +22 -19
data/features/README.md
CHANGED
data/features/Upgrade.md
CHANGED
@@ -36,7 +36,7 @@ is what you need to change:
|
|
36
36
|
|
37
37
|
## Controller specs
|
38
38
|
|
39
|
-
###
|
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.
|
@@ -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 %>.
|
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 %>.
|
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 %>.
|
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 %>.
|
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 %>.
|
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 %>.
|
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 %>.
|
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 %>.
|
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 %>.
|
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
|
data/lib/rspec/rails/adapters.rb
CHANGED
@@ -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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
18
|
+
described_class
|
93
19
|
end
|
94
20
|
|
95
|
-
# Supports a simple DSL for specifying
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
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
|
-
#
|
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
|
-
#
|
104
|
-
# controller do
|
105
|
-
# def index
|
106
|
-
# raise ApplicationController::AccessDenied
|
107
|
-
# end
|
108
|
-
# end
|
33
|
+
# @example
|
109
34
|
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
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
|
-
#
|
122
|
-
#
|
123
|
-
#
|
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][:
|
137
|
-
metadata[:example_group][:
|
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
|
-
|
153
|
-
attr_reader :controller, :routes
|
77
|
+
attr_reader :controller, :routes
|
154
78
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
end
|
79
|
+
module BypassRescue
|
80
|
+
def rescue_with_handler(exception)
|
81
|
+
raise exception
|
159
82
|
end
|
83
|
+
end
|
160
84
|
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
|