rspec-rails 2.0.0.beta.8 → 2.0.0.beta.9.1

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 (61) hide show
  1. data/README.markdown +82 -27
  2. data/Rakefile +7 -7
  3. data/Upgrade.markdown +12 -0
  4. data/VERSION +1 -1
  5. data/autotest/discover.rb +1 -0
  6. data/cucumber.yml +3 -0
  7. data/example_app_template.rb +2 -2
  8. data/features/controller_specs/do_not_render_views.feature +61 -0
  9. data/features/controller_specs/readers.feature +18 -0
  10. data/features/controller_specs/render_views.feature +53 -0
  11. data/features/model_specs/errors_on.feature +32 -0
  12. data/features/model_specs/transactional_examples.feature +2 -2
  13. data/features/view_specs/view_spec.feature +102 -1
  14. data/lib/autotest/rails_rspec2.rb +1 -1
  15. data/lib/generators/rspec/helper/helper_generator.rb +1 -0
  16. data/lib/generators/rspec/helper/templates/helper_spec.rb +3 -3
  17. data/lib/generators/rspec/install/install_generator.rb +7 -7
  18. data/lib/generators/rspec/scaffold/scaffold_generator.rb +10 -1
  19. data/lib/generators/rspec/scaffold/templates/edit_spec.rb +1 -1
  20. data/lib/generators/rspec/scaffold/templates/index_spec.rb +1 -1
  21. data/lib/generators/rspec/scaffold/templates/new_spec.rb +1 -1
  22. data/lib/generators/rspec/scaffold/templates/show_spec.rb +2 -2
  23. data/lib/rspec-rails.rb +12 -0
  24. data/lib/rspec/rails.rb +8 -0
  25. data/lib/rspec/rails/adapters.rb +12 -8
  26. data/lib/rspec/rails/example.rb +1 -0
  27. data/lib/rspec/rails/example/controller_example_group.rb +13 -12
  28. data/lib/rspec/rails/example/helper_example_group.rb +43 -0
  29. data/lib/rspec/rails/example/mailer_example_group.rb +10 -5
  30. data/lib/rspec/rails/example/request_example_group.rb +7 -7
  31. data/lib/rspec/rails/example/view_example_group.rb +44 -62
  32. data/lib/rspec/rails/extensions.rb +1 -0
  33. data/lib/rspec/rails/extensions/active_record/base.rb +46 -0
  34. data/lib/rspec/rails/matchers.rb +28 -3
  35. data/lib/rspec/rails/mocks.rb +3 -3
  36. data/lib/rspec/rails/monkey.rb +1 -0
  37. data/lib/rspec/rails/monkey/action_controller/test_case.rb +153 -145
  38. data/lib/rspec/rails/monkey/action_view/test_case.rb +201 -0
  39. data/lib/rspec/rails/null_resolver.rb +10 -0
  40. data/lib/{generators/rspec/install/templates/lib → rspec/rails}/tasks/rspec.rake +4 -5
  41. data/lib/rspec/rails/transactional_database_support.rb +4 -6
  42. data/lib/rspec/rails/version.rb +1 -1
  43. data/lib/rspec/rails/view_assigns.rb +28 -0
  44. data/lib/rspec/rails/view_rendering.rb +33 -0
  45. data/rspec-rails.gemspec +41 -11
  46. data/spec/rspec/rails/example/controller_example_group_spec.rb +11 -0
  47. data/spec/rspec/rails/example/helper_example_group_spec.rb +20 -0
  48. data/spec/rspec/rails/example/mailer_example_group_spec.rb +11 -0
  49. data/spec/rspec/rails/example/request_example_group_spec.rb +11 -0
  50. data/spec/rspec/rails/example/view_example_group_spec.rb +75 -0
  51. data/spec/rspec/rails/example/view_rendering_spec.rb +68 -0
  52. data/spec/rspec/rails/matchers/errors_on_spec.rb +38 -0
  53. data/spec/rspec/rails/mocks/ar_classes.rb +1 -1
  54. data/spec/rspec/rails/mocks/mock_model_spec.rb +88 -65
  55. data/spec/rspec/rails/mocks/stub_model_spec.rb +1 -1
  56. data/spec/rspec/rails/transactional_database_support_spec.rb +2 -2
  57. data/spec/spec_helper.rb +18 -2
  58. data/spec/support/helpers.rb +20 -0
  59. data/specs.watchr +10 -10
  60. data/templates/generate_stuff.rb +3 -1
  61. metadata +40 -9
@@ -0,0 +1 @@
1
+ require 'rspec/rails/extensions/active_record/base'
@@ -0,0 +1,46 @@
1
+ if defined?(ActiveRecord::Base)
2
+ module RSpec
3
+ module Rails
4
+ module Extensions
5
+ module ActiveRecord
6
+ module ClassMethods
7
+ # :call-seq:
8
+ # ModelClass.should have(:no).records
9
+ # ModelClass.should have(1).record
10
+ # ModelClass.should have(n).records
11
+ #
12
+ # Extension to enhance <tt>should have</tt> on AR Model classes
13
+ def records
14
+ find(:all)
15
+ end
16
+ alias :record :records
17
+ end
18
+
19
+ module InstanceMethods
20
+ # :call-seq:
21
+ # model.should have(:no).errors_on(:attribute)
22
+ # model.should have(1).error_on(:attribute)
23
+ # model.should have(n).errors_on(:attribute)
24
+ #
25
+ # Extension to enhance <tt>should have</tt> on AR Model instances.
26
+ # Calls model.valid? in order to prepare the object's errors
27
+ # object.
28
+ def errors_on(attribute)
29
+ self.valid?
30
+ [self.errors[attribute]].flatten.compact
31
+ end
32
+ alias :error_on :errors_on
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ module ActiveRecord #:nodoc:
40
+ class Base
41
+ extend RSpec::Rails::Extensions::ActiveRecord::ClassMethods
42
+ include RSpec::Rails::Extensions::ActiveRecord::InstanceMethods
43
+ end
44
+ end
45
+ end
46
+
@@ -17,21 +17,46 @@ rescue LoadError
17
17
 
18
18
  end
19
19
 
20
- Rspec::Matchers.define :redirect_to do |destination|
20
+ RSpec::Matchers.define :redirect_to do |destination|
21
21
  match_unless_raises Test::Unit::AssertionFailedError do |_|
22
22
  assert_redirected_to destination
23
23
  end
24
24
  end
25
25
 
26
- Rspec::Matchers.define :render_template do |options, message|
26
+ RSpec::Matchers.define :render_template do |options, message|
27
27
  match_unless_raises Test::Unit::AssertionFailedError do |_|
28
28
  options = options.to_s if Symbol === options
29
29
  assert_template options, message
30
30
  end
31
31
  end
32
32
 
33
- Rspec::Matchers.define :be_a_new do |model_klass|
33
+ RSpec::Matchers.define :be_a_new do |model_klass|
34
34
  match do |actual|
35
35
  model_klass === actual && actual.new_record?
36
36
  end
37
37
  end
38
+
39
+ require 'rspec/matchers/have'
40
+
41
+ module RSpec #:nodoc:
42
+ module Matchers #:nodoc:
43
+ class Have #:nodoc:
44
+
45
+ def failure_message_for_should_with_errors_on_extensions
46
+ return "expected #{relativities[@relativity]}#{@expected} errors on :#{@args[0]}, got #{@actual}" if @collection_name == :errors_on
47
+ return "expected #{relativities[@relativity]}#{@expected} error on :#{@args[0]}, got #{@actual}" if @collection_name == :error_on
48
+ return failure_message_for_should_without_errors_on_extensions
49
+ end
50
+ alias_method_chain :failure_message_for_should, :errors_on_extensions
51
+
52
+ def description_with_errors_on_extensions
53
+ return "have #{relativities[@relativity]}#{@expected} errors on :#{@args[0]}" if @collection_name == :errors_on
54
+ return "have #{relativities[@relativity]}#{@expected} error on :#{@args[0]}" if @collection_name == :error_on
55
+ return description_without_errors_on_extensions
56
+ end
57
+ alias_method_chain :description, :errors_on_extensions
58
+
59
+ end
60
+ end
61
+ end
62
+
@@ -1,4 +1,4 @@
1
- module Rspec
1
+ module RSpec
2
2
  module Rails
3
3
 
4
4
  class IllegalDataAccessException < StandardError; end
@@ -140,6 +140,6 @@ module Rspec
140
140
  end
141
141
  end
142
142
 
143
- Rspec.configure do |c|
144
- c.include Rspec::Rails::Mocks
143
+ RSpec.configure do |c|
144
+ c.include RSpec::Rails::Mocks
145
145
  end
@@ -1,2 +1,3 @@
1
1
  require 'rspec/rails/monkey/action_controller/test_case'
2
+ require 'rspec/rails/monkey/action_view/test_case'
2
3
  require 'rspec/rails/monkey/active_support/notifications/fanout'
@@ -7,193 +7,201 @@ module ActionController
7
7
  # Once 3.0.0.rc.1 comes out, we can remove it.
8
8
  module TemplateAssertions
9
9
  def teardown_subscriptions
10
+ # rails-3.0.0.beta.3
10
11
  ActiveSupport::Notifications.unsubscribe("action_view.render_template")
11
12
  ActiveSupport::Notifications.unsubscribe("action_view.render_template!")
13
+
14
+ # as of 731d4392e478ff5526b595074d9caa999da8bd0c
15
+ ActiveSupport::Notifications.unsubscribe("render_template.action_view")
16
+ ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
12
17
  end
13
18
  end
14
19
 
15
- # The remainder of this file has yet to be merged to rails HEAD and is
16
- # therefore merely speculative and hopeful.
17
- class TestCase < ActiveSupport::TestCase
18
- module Behavior
19
- extend ActiveSupport::Concern
20
- include ActionDispatch::TestProcess
20
+ # This has been merged to rails HEAD after the 3.0.0.beta.3 release (see
21
+ # https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4474).
22
+ # Once 3.0.0.rc.1 comes out, we can remove it.
23
+ unless defined?(ActionController::TestCase::Behavior)
24
+ class TestCase < ActiveSupport::TestCase
25
+ module Behavior
26
+ extend ActiveSupport::Concern
27
+ include ActionDispatch::TestProcess
28
+
29
+ attr_reader :response, :request
30
+
31
+ module ClassMethods
32
+
33
+ # Sets the controller class name. Useful if the name can't be inferred from test class.
34
+ # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
35
+ def tests(controller_class)
36
+ self.controller_class = controller_class
37
+ end
38
+
39
+ def controller_class=(new_class)
40
+ prepare_controller_class(new_class) if new_class
41
+ write_inheritable_attribute(:controller_class, new_class)
42
+ end
43
+
44
+ def controller_class
45
+ if current_controller_class = read_inheritable_attribute(:controller_class)
46
+ current_controller_class
47
+ else
48
+ self.controller_class = determine_default_controller_class(name)
49
+ end
50
+ end
21
51
 
22
- attr_reader :response, :request
52
+ def determine_default_controller_class(name)
53
+ name.sub(/Test$/, '').constantize
54
+ rescue NameError
55
+ nil
56
+ end
23
57
 
24
- module ClassMethods
58
+ def prepare_controller_class(new_class)
59
+ new_class.send :include, ActionController::TestCase::RaiseActionExceptions
60
+ end
25
61
 
26
- # Sets the controller class name. Useful if the name can't be inferred from test class.
27
- # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>.
28
- def tests(controller_class)
29
- self.controller_class = controller_class
30
62
  end
31
-
32
- def controller_class=(new_class)
33
- prepare_controller_class(new_class) if new_class
34
- write_inheritable_attribute(:controller_class, new_class)
63
+
64
+ # Executes a request simulating GET HTTP method and set/volley the response
65
+ def get(action, parameters = nil, session = nil, flash = nil)
66
+ process(action, parameters, session, flash, "GET")
35
67
  end
36
68
 
37
- def controller_class
38
- if current_controller_class = read_inheritable_attribute(:controller_class)
39
- current_controller_class
40
- else
41
- self.controller_class = determine_default_controller_class(name)
42
- end
69
+ # Executes a request simulating POST HTTP method and set/volley the response
70
+ def post(action, parameters = nil, session = nil, flash = nil)
71
+ process(action, parameters, session, flash, "POST")
43
72
  end
44
73
 
45
- def determine_default_controller_class(name)
46
- name.sub(/Test$/, '').constantize
47
- rescue NameError
48
- nil
74
+ # Executes a request simulating PUT HTTP method and set/volley the response
75
+ def put(action, parameters = nil, session = nil, flash = nil)
76
+ process(action, parameters, session, flash, "PUT")
49
77
  end
50
78
 
51
- def prepare_controller_class(new_class)
52
- new_class.send :include, ActionController::TestCase::RaiseActionExceptions
79
+ # Executes a request simulating DELETE HTTP method and set/volley the response
80
+ def delete(action, parameters = nil, session = nil, flash = nil)
81
+ process(action, parameters, session, flash, "DELETE")
53
82
  end
54
83
 
55
- end
84
+ # Executes a request simulating HEAD HTTP method and set/volley the response
85
+ def head(action, parameters = nil, session = nil, flash = nil)
86
+ process(action, parameters, session, flash, "HEAD")
87
+ end
56
88
 
57
- # Executes a request simulating GET HTTP method and set/volley the response
58
- def get(action, parameters = nil, session = nil, flash = nil)
59
- process(action, parameters, session, flash, "GET")
60
- end
89
+ def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
90
+ @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
91
+ @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
92
+ returning __send__(request_method, action, parameters, session, flash) do
93
+ @request.env.delete 'HTTP_X_REQUESTED_WITH'
94
+ @request.env.delete 'HTTP_ACCEPT'
95
+ end
96
+ end
97
+ alias xhr :xml_http_request
98
+
99
+ def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
100
+ # Sanity check for required instance variables so we can give an
101
+ # understandable error message.
102
+ %w(@routes @controller @request @response).each do |iv_name|
103
+ if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
104
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
105
+ end
106
+ end
61
107
 
62
- # Executes a request simulating POST HTTP method and set/volley the response
63
- def post(action, parameters = nil, session = nil, flash = nil)
64
- process(action, parameters, session, flash, "POST")
65
- end
108
+ @request.recycle!
109
+ @response.recycle!
110
+ @controller.response_body = nil
111
+ @controller.formats = nil
112
+ @controller.params = nil
66
113
 
67
- # Executes a request simulating PUT HTTP method and set/volley the response
68
- def put(action, parameters = nil, session = nil, flash = nil)
69
- process(action, parameters, session, flash, "PUT")
70
- end
114
+ @html_document = nil
115
+ @request.env['REQUEST_METHOD'] = http_method
71
116
 
72
- # Executes a request simulating DELETE HTTP method and set/volley the response
73
- def delete(action, parameters = nil, session = nil, flash = nil)
74
- process(action, parameters, session, flash, "DELETE")
75
- end
117
+ parameters ||= {}
118
+ @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
76
119
 
77
- # Executes a request simulating HEAD HTTP method and set/volley the response
78
- def head(action, parameters = nil, session = nil, flash = nil)
79
- process(action, parameters, session, flash, "HEAD")
80
- end
120
+ @request.session = ActionController::TestSession.new(session) unless session.nil?
121
+ @request.session["flash"] = @request.flash.update(flash || {})
122
+ @request.session["flash"].sweep
81
123
 
82
- def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
83
- @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
84
- @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
85
- returning __send__(request_method, action, parameters, session, flash) do
86
- @request.env.delete 'HTTP_X_REQUESTED_WITH'
87
- @request.env.delete 'HTTP_ACCEPT'
88
- end
89
- end
90
- alias xhr :xml_http_request
91
-
92
- def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
93
- # Sanity check for required instance variables so we can give an
94
- # understandable error message.
95
- %w(@routes @controller @request @response).each do |iv_name|
96
- if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
97
- raise "#{iv_name} is nil: make sure you set it in your test's setup method."
98
- end
124
+ @controller.request = @request
125
+ @controller.params.merge!(parameters)
126
+ build_request_uri(action, parameters)
127
+ Base.class_eval { include Testing }
128
+ @controller.process_with_new_base_test(@request, @response)
129
+ @request.session.delete('flash') if @request.session['flash'].blank?
130
+ @response
99
131
  end
100
132
 
101
- @request.recycle!
102
- @response.recycle!
103
- @controller.response_body = nil
104
- @controller.formats = nil
105
- @controller.params = nil
106
-
107
- @html_document = nil
108
- @request.env['REQUEST_METHOD'] = http_method
109
-
110
- parameters ||= {}
111
- @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
112
-
113
- @request.session = ActionController::TestSession.new(session) unless session.nil?
114
- @request.session["flash"] = @request.flash.update(flash || {})
115
- @request.session["flash"].sweep
116
-
117
- @controller.request = @request
118
- @controller.params.merge!(parameters)
119
- build_request_uri(action, parameters)
120
- Base.class_eval { include Testing }
121
- @controller.process_with_new_base_test(@request, @response)
122
- @request.session.delete('flash') if @request.session['flash'].blank?
123
- @response
124
- end
133
+ def setup_controller_request_and_response
134
+ @request = TestRequest.new
135
+ @response = TestResponse.new
125
136
 
126
- def setup_controller_request_and_response
127
- @request = TestRequest.new
128
- @response = TestResponse.new
129
-
130
- if klass = self.class.controller_class
131
- @controller ||= klass.new rescue nil
132
- end
137
+ if klass = self.class.controller_class
138
+ @controller ||= klass.new rescue nil
139
+ end
133
140
 
134
- @request.env.delete('PATH_INFO')
141
+ @request.env.delete('PATH_INFO')
135
142
 
136
- if @controller
137
- @controller.request = @request
138
- @controller.params = {}
143
+ if @controller
144
+ @controller.request = @request
145
+ @controller.params = {}
146
+ end
139
147
  end
140
- end
141
148
 
142
- # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
143
- def rescue_action_in_public!
144
- @request.remote_addr = '208.77.188.166' # example.com
145
- end
149
+ # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local
150
+ def rescue_action_in_public!
151
+ @request.remote_addr = '208.77.188.166' # example.com
152
+ end
146
153
 
147
- included do
148
- include ActionController::TemplateAssertions
149
- include ActionDispatch::Assertions
150
- setup :setup_controller_request_and_response
151
- end
154
+ included do
155
+ include ActionController::TemplateAssertions
156
+ include ActionDispatch::Assertions
157
+ setup :setup_controller_request_and_response
158
+ end
152
159
 
153
- private
160
+ private
154
161
 
155
- def build_request_uri(action, parameters)
156
- unless @request.env["PATH_INFO"]
157
- options = @controller.__send__(:url_options).merge(parameters)
158
- options.update(
159
- :only_path => true,
160
- :action => action,
161
- :relative_url_root => nil,
162
- :_path_segments => @request.symbolized_path_parameters)
162
+ def build_request_uri(action, parameters)
163
+ unless @request.env["PATH_INFO"]
164
+ options = @controller.__send__(:url_options).merge(parameters)
165
+ options.update(
166
+ :only_path => true,
167
+ :action => action,
168
+ :relative_url_root => nil,
169
+ :_path_segments => @request.symbolized_path_parameters)
163
170
 
164
- url, query_string = @routes.url_for(options).split("?", 2)
171
+ url, query_string = @routes.url_for(options).split("?", 2)
165
172
 
166
- @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
167
- @request.env["PATH_INFO"] = url
168
- @request.env["QUERY_STRING"] = query_string || ""
173
+ @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
174
+ @request.env["PATH_INFO"] = url
175
+ @request.env["QUERY_STRING"] = query_string || ""
176
+ end
169
177
  end
170
178
  end
171
- end
172
179
 
173
- # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
174
- # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
175
- # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
176
- # than 0.0.0.0.
177
- #
178
- # The exception is stored in the exception accessor for further inspection.
179
- module RaiseActionExceptions
180
- def self.included(base)
181
- base.class_eval do
182
- attr_accessor :exception
183
- protected :exception, :exception=
180
+ # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
181
+ # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
182
+ # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
183
+ # than 0.0.0.0.
184
+ #
185
+ # The exception is stored in the exception accessor for further inspection.
186
+ module RaiseActionExceptions
187
+ def self.included(base)
188
+ base.class_eval do
189
+ attr_accessor :exception
190
+ protected :exception, :exception=
191
+ end
184
192
  end
185
- end
186
193
 
187
- protected
188
- def rescue_action_without_handler(e)
189
- self.exception = e
194
+ protected
195
+ def rescue_action_without_handler(e)
196
+ self.exception = e
190
197
 
191
- if request.remote_addr == "0.0.0.0"
192
- raise(e)
193
- else
194
- super(e)
198
+ if request.remote_addr == "0.0.0.0"
199
+ raise(e)
200
+ else
201
+ super(e)
202
+ end
195
203
  end
196
- end
204
+ end
197
205
  end
198
206
  end
199
207
  end